日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Mail.Ru Cup 2018 Round 2

發布時間:2023/12/3 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Mail.Ru Cup 2018 Round 2 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Mail.Ru Cup 2018 Round 2


C. Lucky Days

題意:找出最長的一段連續區間,同時被\([l_a + k_at_a, r_a + k_at_a]\) , \([l_b + k_bt_b, r_b + k_bt_b]\)覆蓋。

做法:設最終的答案為\([L,R]\),那么\(L\)一定是\(l_a + k_at_a,~~ l_b + k_bt_b\), \(R\)同理。根據不同條件,解不等式,然后判斷是否有解即可。其中\(k_ata - k_bt_b = k* gcd(t_a,t_b)\)

#include <bits/stdc++.h> #define VI vector<int> #define VL vector<ll> #define P pair<ll,int> #define fr first #define sc second #define pb push_back #define rep(i,a,b) for(int i = a; i <= b; ++i) #define per(i,a,b) for(int i = a; i >= b; --i) #define mem(a,b) memset(a,b,sizeof(b)) typedef long long ll; const ll inf = 1000000000000000000LL; using namespace std; ll ra,la,ta,rb,lb,tb,ans; ll fd(ll x,ll g) {ll t;t = x/g, t*=g;while(t < x) t+=g;return t; } int main() {scanf("%lld%lld%lld",&la,&ra,&ta);scanf("%lld%lld%lld",&lb,&rb,&tb);ll g = __gcd(ta,tb);if(ra-rb <= la - lb && fd(ra-rb,g) <= la-lb ) {ans = max(ra-la+1,ans);printf("%lld\n",ans); return 0;}if(rb-ra <= lb - la && fd(rb-ra,g) <= lb-la ) {ans = max(rb-lb+1,ans);printf("%lld\n",ans); return 0;}ll mx = max(la-lb,la-rb);//cout << mx << ' '<< ra-rb << endl;if(fd(mx,g) <= (ra-lb)) {ll X = fd(mx,g); // cout << "ff" << endl;ans = max((ra-lb+1) - X,ans);}mx = max(lb-la,lb-ra);if(fd(mx,g) <= (rb-la)) {ll X = fd(mx,g); // cout << "f";ans = max((rb-la+1) - X,ans);}printf("%lld\n",ans);return 0; }

D. Refactoring

題意:對第一組的每個串,第一次出現的串s,將它變成t,可以獲得第二組串,現在給定兩組串求s和t。

做法:對每個串都取確定了一些轉換,先找到每個串第一個轉換關系,然后盡量向兩邊擴展這個串,然后就可以確定一個s,再對每個第一組串跑kmp驗證即可。慎用strstr()...

#include <bits/stdc++.h> typedef long long ll; const int N = 3003; using namespace std; int n, l[N], st[N], ed[N], idx, cc; char s1[N][N], s2[N][N], str[N]; int ck0() {for(int i = 1; i <= n; ++i) if(st[i] != 0) {if(s1[i][st[i]] == s1[idx][st[idx]] && s2[i][st[i]] == s2[idx][st[idx]] ) continue;else return 0;}return 1; } int ck1() {for(int i = 1; i <= n; ++i) if(st[i] != 0) {if(st[i] == 1) return 0;else {if(s1[i][st[i]-1] == s1[idx][st[idx]-1] && s2[i][st[i]-1] == s2[idx][st[idx]-1] ) continue;else return 0;}}return 1; } int ck2() {for(int i = 1; i <= n; ++i) if(st[i] != 0) {if(ed[i] == l[i]) return 0;else {if(s1[i][ed[i]+1] == s1[idx][ed[idx]+1] && s2[i][ed[i]+1] == s2[idx][ed[idx]+1] ) continue;else return 0;}}return 1; }int nxt[N]; void getnxt(char s[],int n){nxt[1]=0;for(int i=2;i<=n;i++){int j=nxt[i-1];while(j&&s[j+1]!=s[i]) j=nxt[j];if(s[j+1]==s[i]) nxt[i]=j+1;else nxt[i] = 0;} } int kmp(char p[],int m, char s[], int n){int j=0;for(int i=1;i<=n;i++){while(j&&p[j+1]!=s[i]) j=nxt[j];if(p[j+1]==s[i])++j;if(j==m) { return i-m+1;}}return -1; } int main() {scanf("%d",&n);for(int i = 1; i <= n; ++i) scanf(" %s",s1[i]+1), l[i] = strlen(s1[i]+1);for(int i = 1; i <= n; ++i) scanf(" %s",s2[i]+1);for(int i = 1; i <= n; ++i) {for(int j = 1; j <= l[i]; ++j) if(s1[i][j] != s2[i][j]) {idx = i;st[i] = ed[i] = j;break;}}if(idx == 0) {puts("YES"); printf("a\na\n");return 0;}if(!ck0()) {return puts("NO"),0;}while(ck1()) {for(int i = 1; i <= n; ++i)if(st[i]!=0) --st[i];}while(ck2()) {for(int i = 1; i <= n; ++i)if(ed[i]!=0) ++ed[i];}for(int i = st[idx]; i <= ed[idx]; ++i) str[++cc] = s1[idx][i]; str[cc+1] = '\0';getnxt(str,cc);for(int i = 1; i <= n; ++i) {int p = kmp(str,cc,s1[i],l[i]);if(st[i] != 0) {if(p != st[i]) return puts("NO"),0;}else if(p != -1) {return puts("NO"),0;}}puts("YES");printf("%s\n",str+1);for(int i = st[idx]; i <= ed[idx]; ++i) printf("%c",s2[idx][i]);puts("");return 0; }

E. Segments on the Line

題意:給定一個長度為\(n\)的序列\(a\),以及\(s\)個區間\([l_i,r_i]\),從這些區間中,挑恰好\(m\)個區間,他們的并區間中元素第\(k\)小的值為這種方案的答案,求最小的答案。

做法:首先,二分第\(k\)小的值\(v\),然后問題就轉換為求,挑\(m\)個區間他們中小于\(v\)的數最多是多少。將區間按照右端點排序,\(dp[i][j]\)表示前\(i\)個區間選了\(j\)個,且第\(i\)個一定選了的小于\(v\)的數目,轉移有兩種:1)第\(i\)個區間與前一個區間不相交,這個需要維護每個位置向前,用了\(j\)個區間,的\(dp\)的最大值;2)第\(i\)個區間與前一個區間不相交,這時取上一個區間取左端點盡可能小的一定更優,因為較大的左端點一定可以被他包含,對每個區間維護一下前一個不相交的區間,和相交的中左端點最小的區間。還有一種,情況有區間相互包含的情況,顯然取大的更優。(我的實現有點麻煩。。懶得改了。

#include <bits/stdc++.h> #define pb push_back typedef long long ll; const int N = 1600; using namespace std; int n,s,m,k,a[N]; vector<int> v[N]; struct node{ int l,r; } b[N]; bool cmp(node a,node b) {if(a.r != b.r) return a.r < b.r;return a.l < b.l; } int dp[N][N], MX[N][N], pre[N], pl[N]; int cal(int x) {int ans = 0;memset(dp,0,sizeof(dp));dp[1][1] = MX[1][1] = upper_bound(v[1].begin(),v[1].end(),x) - v[1].begin();for(int i = 1,c=0,c2=0; i <= s; ++i) {c = c2 = 0;if(pl[i] != 0) {for(int j = b[pl[i]].r+1; j <= b[i].r; ++j) c2 += (a[j] <= x);}c = upper_bound(v[i].begin(),v[i].end(),x) - v[i].begin();dp[i][1] = c;for(int j = 2; j <= i && j <= m; ++j) {if(pre[i] != 0) dp[i][j] = max(dp[i][j], MX[pre[i]][j-1] + c);if(pl[i] != 0) dp[i][j] = max(dp[i][j], dp[pl[i]][j-1] + c2);dp[i][j] = max(dp[i][j], dp[i][j-1]);}for(int j = 1; j <= i && j <= m; ++j) MX[i][j] = max(MX[i-1][j], dp[i][j]);ans = max(ans, dp[i][m]);}return ans; }int main() {scanf("%d%d%d%d",&n,&s,&m,&k);for(int i = 1; i <= n; ++i) scanf("%d",&a[i]);for(int i = 1; i <= s; ++i) {scanf("%d%d",&b[i].l,&b[i].r);}sort(b+1, b+1+s, cmp);for(int i = 1; i <= s; ++i) { for(int j = b[i].l ; j <= b[i].r ; ++j) v[i].pb(a[j]);sort(v[i].begin(),v[i].end());}for(int i = 1; i <= s; ++i) {int mx = 0, idx = 0, mn = 1000000, idx2 = 0;for(int j = 1; j < i; ++j) {if(b[j].r < b[i].l && mx < b[j].r) {mx = b[j].r; idx = j;}if(b[j].r >= b[i].l && mn > b[j].l) {mn = b[j].l; idx2 = j;}}pre[i] = idx; pl[i] = idx2;}int l = 1, r = 1e9, mid, ans = -1;while(l <= r) {ll mid = (l + r) >> 1;if(cal(mid) >= k) r = mid-1, ans = mid;else l = mid + 1;}printf("%d\n",ans);return 0; }

F. Tree and XOR

題意:給定一棵\(n\)節點的樹,每條邊都有權值,一條從\(u\)\(v\)的路徑的價值定義為路徑上邊權的異或和,問所有\(n^2\)個路徑中,第\(k\)小的路徑的異或和是多少。

做法:兩點之間路徑的價值,可以通過他們到根的前綴異或和相異或求得。于是,問題轉化為\(n\)個數,問他們兩兩之間的\(n^2\)個異或值中,第\(k\)小的是什么。首先,一個思路就是,二分答案,然后枚舉一個數,在\(trie\)樹上查詢比這個數小的數的個數。復雜度是有兩個\(log\)。考慮直接通過\(trie\)樹,確定答案的每一位是什么,對于每個數維護他當前所在的\(Trie\)樹上的節點,計算當前層異或為\(0\)的數目\(sum\),如果\(k>sum\),那么這一位是\(1\),否則是\(0\)。然后,更新每個數,在\(Trie\)樹上的位置。這樣時間可以做到\(O(62logn)\),但是,空間依然不夠,看了別人的代碼,可以換一種實現,在建\(Trie\)樹的同時,計算,每次先插入當前層的數,建好當前層,再枚舉計算當前位異或為\(0\)的數目,更新每個數的位置。注意到我們每次使用的節點只有上一層的,那么就可以每次只保留最后一層的節點,新的節點可以復用之前的空間

#include <bits/stdc++.h> typedef long long ll; const int N = 1000100; using namespace std; int n; ll k,w[N],ans; int rt[N], prt[N], num[N], cc, ch[N][2]; int main() {scanf("%d%lld",&n,&k);for(int p,i = 2; i <= n; ++i) scanf("%d%lld",&p,&w[i]), w[i]^=w[p];for(int i = 1; i <= n; ++i) rt[i] = prt[i] = 1;for(int s = 61; s >= 0; --s) {for(int i = 1; i <= cc; ++i) ch[i][0]=ch[i][1]=num[i]=0; cc = 1;for(int i = 1; i <= n; ++i) {int t = (w[i]>>s)&1;if(!ch[rt[i]][t]) ch[rt[i]][t] = ++cc;rt[i] = ch[rt[i]][t];num[rt[i]]++;}ll sum = 0;for(int i = 1; i <= n; ++i) {int t = (w[i]>>s)&1;sum += num[ch[prt[i]][t]];}if(sum < k) {ans |= (1LL<<s);k-=sum;for(int i = 1; i <= n; ++i) {int t = (w[i]>>s)&1;prt[i] = ch[prt[i]][t^1];}}else {for(int i = 1; i <= n; ++i) {int t = (w[i]>>s)&1;prt[i] = ch[prt[i]][t];}}}printf("%lld\n",ans);return 0; }

轉載于:https://www.cnblogs.com/RRRR-wys/p/9969522.html

總結

以上是生活随笔為你收集整理的Mail.Ru Cup 2018 Round 2的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。