Mail.Ru Cup 2018 Round 2
Mail.Ru Cup 2018 Round 2
C. Lucky Days
題意:找出最長(zhǎng)的一段連續(xù)區(qū)間,同時(shí)被\([l_a + k_at_a, r_a + k_at_a]\) , \([l_b + k_bt_b, r_b + k_bt_b]\)覆蓋。
做法:設(shè)最終的答案為\([L,R]\),那么\(L\)一定是\(l_a + k_at_a,~~ l_b + k_bt_b\), \(R\)同理。根據(jù)不同條件,解不等式,然后判斷是否有解即可。其中\(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
題意:對(duì)第一組的每個(gè)串,第一次出現(xiàn)的串s,將它變成t,可以獲得第二組串,現(xiàn)在給定兩組串求s和t。
做法:對(duì)每個(gè)串都取確定了一些轉(zhuǎn)換,先找到每個(gè)串第一個(gè)轉(zhuǎn)換關(guān)系,然后盡量向兩邊擴(kuò)展這個(gè)串,然后就可以確定一個(gè)s,再對(duì)每個(gè)第一組串跑kmp驗(yàn)證即可。慎用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
題意:給定一個(gè)長(zhǎng)度為\(n\)的序列\(a\),以及\(s\)個(gè)區(qū)間\([l_i,r_i]\),從這些區(qū)間中,挑恰好\(m\)個(gè)區(qū)間,他們的并區(qū)間中元素第\(k\)小的值為這種方案的答案,求最小的答案。
做法:首先,二分第\(k\)小的值\(v\),然后問(wèn)題就轉(zhuǎn)換為求,挑\(m\)個(gè)區(qū)間他們中小于\(v\)的數(shù)最多是多少。將區(qū)間按照右端點(diǎn)排序,\(dp[i][j]\)表示前\(i\)個(gè)區(qū)間選了\(j\)個(gè),且第\(i\)個(gè)一定選了的小于\(v\)的數(shù)目,轉(zhuǎn)移有兩種:1)第\(i\)個(gè)區(qū)間與前一個(gè)區(qū)間不相交,這個(gè)需要維護(hù)每個(gè)位置向前,用了\(j\)個(gè)區(qū)間,的\(dp\)的最大值;2)第\(i\)個(gè)區(qū)間與前一個(gè)區(qū)間不相交,這時(shí)取上一個(gè)區(qū)間取左端點(diǎn)盡可能小的一定更優(yōu),因?yàn)檩^大的左端點(diǎn)一定可以被他包含,對(duì)每個(gè)區(qū)間維護(hù)一下前一個(gè)不相交的區(qū)間,和相交的中左端點(diǎn)最小的區(qū)間。還有一種,情況有區(qū)間相互包含的情況,顯然取大的更優(yōu)。(我的實(shí)現(xiàn)有點(diǎn)麻煩。。懶得改了。
#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\)節(jié)點(diǎn)的樹(shù),每條邊都有權(quán)值,一條從\(u\)到\(v\)的路徑的價(jià)值定義為路徑上邊權(quán)的異或和,問(wèn)所有\(n^2\)個(gè)路徑中,第\(k\)小的路徑的異或和是多少。
做法:兩點(diǎn)之間路徑的價(jià)值,可以通過(guò)他們到根的前綴異或和相異或求得。于是,問(wèn)題轉(zhuǎn)化為\(n\)個(gè)數(shù),問(wèn)他們兩兩之間的\(n^2\)個(gè)異或值中,第\(k\)小的是什么。首先,一個(gè)思路就是,二分答案,然后枚舉一個(gè)數(shù),在\(trie\)樹(shù)上查詢比這個(gè)數(shù)小的數(shù)的個(gè)數(shù)。復(fù)雜度是有兩個(gè)\(log\)。考慮直接通過(guò)\(trie\)樹(shù),確定答案的每一位是什么,對(duì)于每個(gè)數(shù)維護(hù)他當(dāng)前所在的\(Trie\)樹(shù)上的節(jié)點(diǎn),計(jì)算當(dāng)前層異或?yàn)?span id="ze8trgl8bvbq" class="math inline">\(0\)的數(shù)目\(sum\),如果\(k>sum\),那么這一位是\(1\),否則是\(0\)。然后,更新每個(gè)數(shù),在\(Trie\)樹(shù)上的位置。這樣時(shí)間可以做到\(O(62logn)\),但是,空間依然不夠,看了別人的代碼,可以換一種實(shí)現(xiàn),在建\(Trie\)樹(shù)的同時(shí),計(jì)算,每次先插入當(dāng)前層的數(shù),建好當(dāng)前層,再枚舉計(jì)算當(dāng)前位異或?yàn)?span id="ze8trgl8bvbq" class="math inline">\(0\)的數(shù)目,更新每個(gè)數(shù)的位置。注意到我們每次使用的節(jié)點(diǎn)只有上一層的,那么就可以每次只保留最后一層的節(jié)點(diǎn),新的節(jié)點(diǎn)可以復(fù)用之前的空間
#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; }轉(zhuǎn)載于:https://www.cnblogs.com/RRRR-wys/p/9969522.html
總結(jié)
以上是生活随笔為你收集整理的Mail.Ru Cup 2018 Round 2的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 新手小白最容易上手的视频制作教程新手小白
- 下一篇: Educational Codeforc