Codeforces Round #545 (div 1.)
B.Camp Schedule
给两个 $01$ 串 $s$ 和 $t$,你可以将 $s$ 串任意重排,要求最大化 $t$ 在 $s$ 子串中出现的次数,可以重叠
$|s|,|t| \leq 500000$
sol:
看到可以重叠,考虑最大化利用重叠部分
重叠部分是这次 $t$ 串的结束和下次 $t$ 串的开始,也就是 $t$ 串的一个 $border$
先放一个 $border$ ,之后一直放 $t$ 串的除 $border$ 以外的部分就可以了
#include <bits/stdc++.h> #define LL long long using namespace std; #define rep(i, s, t) for (register int i = (s), i##end = (t); i <= i##end; ++i) #define dwn(i, s, t) for (register int i = (s), i##end = (t); i >= i##end; --i) inline int read() { int x = 0, f = 1; char ch = getchar(); for (; !isdigit(ch); ch = getchar())if (ch == '-')f = -f; for (; isdigit(ch); ch = getchar()) x = 10 * x + ch - '0'; return x * f; } char s[500010], t[500010], border[500010], ans[500010], pborder[500010]; int fail[500010], cnt[2], pcnt[2], ncnt[2]; int chk() {return (cnt[1] >= pcnt[1]) && (cnt[0] >= pcnt[0]);} int chk2() {return (cnt[1] >= ncnt[1]) && (cnt[0] >= ncnt[0]);} int main() { scanf("%s", s + 1); scanf("%s", t + 1); int n = strlen(s + 1), m = strlen(t + 1); rep(i, 1, n) cnt[s[i] - '0']++; int j = 0; for(int i=2;i<=m;i++) { while(j && t[j + 1] != t[i]) j = fail[j]; if(t[i] == t[j + 1]) j++; fail[i] = j; } int blen = fail[m]; rep(i, 1, blen) border[i] = t[i], pcnt[t[i] - '0']++; //cout << border+1 << endl; rep(i, blen+1, m) pborder[i - blen] = t[i], ncnt[t[i] - '0']++; //cout << pborder+1 << endl; int clen = strlen(pborder + 1); int dfn = 1; if(chk()) { cnt[1] -= pcnt[1]; cnt[0] -= pcnt[0]; rep(i, 1, blen) ans[i] = border[i]; dfn = blen; while(chk2()) { rep(i, 1, clen) ans[dfn + i] = pborder[i]; dfn += clen; cnt[1] -= ncnt[1]; cnt[0] -= ncnt[0]; } while(cnt[1]) { ans[++dfn] = '1'; cnt[1]--; } while(cnt[0]) { ans[++dfn] = '0'; cnt[0]--; } cout << (ans + 1) << endl; } else { cout << (s+1) << endl; } } /*1111111001100 101*/
C.Museums Tour
一个有向图,每个点有一个博物馆,每个博物馆有一个长度为 $d$ 的开放时间表(是循环的),你可以进行一次任意天的旅行,但是每天必须走一步,问最多能参观多少博物馆
$n,m \leq 100000,d \leq 50$
sol:
卡空间神题,卡掉了 w \times h
虽然他还是 rank 1.
有很多基于强连通分量的做法,这里写一种比较稳的
把每个点拆成 $d$ 个点,求一个强连通分量
在强连通分量里算一下有哪些点可以走到即可,显然只需要 $(x,y)$ 这个点在强连通分量里且 $x$ 在第 $y$ 天开放就可以通过一直绕圈圈走到
外面是个 DAG,做个 dp 即可
#include <bits/stdc++.h> #define LL long long using namespace std; #define rep(i, s, t) for (register int i = (s), i##end = (t); i <= i##end; ++i) #define dwn(i, s, t) for (register int i = (s), i##end = (t); i >= i##end; --i) inline int read() { int x = 0, f = 1; char ch = getchar(); for (; !isdigit(ch); ch = getchar())if (ch == '-')f = -f; for (; isdigit(ch); ch = getchar()) x = 10 * x + ch - '0'; return x * f; } const int maxn = 5000010; int n, m, d; int first[maxn], to[maxn], nx[maxn], cnt; inline void add(int u, int v) { to[++cnt] = v; nx[cnt] = first[u]; first[u] = cnt; } int dfn[maxn], low[maxn], st[maxn], top, bl[maxn], scc, _tim; char grid[100010][60]; int now; vector<int> G2[maxn], ccs[maxn]; inline void Tarjan(int x) { dfn[x] = low[x] = ++_tim; st[++top] = x; for(int i=first[x];i;i=nx[i]) { if(!dfn[to[i]]) { Tarjan(to[i]); low[x] = min(low[x], low[to[i]]); } else if(bl[to[i]] == -1) low[x] = min(low[x], dfn[to[i]]); } if(low[x] == dfn[x]) { now = -1; do { now = st[top--]; bl[now] = scc; ccs[scc].push_back(now); }while(now != x); scc++; } } int dp[maxn], hsh[maxn]; int main() { n = read(), m = read(), d = read(); memset(bl, -1, sizeof(bl)); rep(i, 0, m-1) { int u = read() - 1, v = read() - 1; rep(j, 0, d-1) add(u * d + j, v * d + (j + 1) % d); } rep(i, 0, n-1) scanf("%s", grid[i]); rep(i, 0, n-1) rep(j, 0, d-1) grid[i][j] -= '0'; rep(i, 0, n*d-1) if(!dfn[i]) Tarjan(i); rep(i, 0, scc-1) { sort(ccs[i].begin(), ccs[i].end()); int last = -1; // cout << i << ":" << endl; // for(auto j : ccs[i]) cout << j << " "; // cout << endl; for(auto j : ccs[i]) { if(j / d != last) { if(grid[j / d][j % d]) { //cout << last << ":" << j << " " << d << endl; hsh[i]++; last = j / d; } } } } rep(x, 0, n*d-1) for(int i=first[x];i;i=nx[i]) if(bl[x] != bl[to[i]]) G2[bl[x]].push_back(bl[to[i]]); //cout << bl[x] << "->" << bl[to[i]] << endl; //ind[bl[to[i]]]++; //rep(i, 0, scc-1) cout << hsh[i] << " "; rep(i, 0, scc-1) { dp[i] = hsh[i]; for(auto h : G2[i]) dp[i] = max(dp[i], dp[h] + hsh[i]); } cout << dp[bl[0]] << endl; }
D.Cooperative Game
交互题
有一个链表,已知它有闭环,你一开始有 $10$ 个指针,都指向表头
每次操作你可以让若干个指针向前移动一个单位,交互器会返回现在哪些指针在同一个单位
你现在要让所有指针都正好在环的起始节点,并在都在环的起始节点时输出 $done$
操作次数限制为 $3n$ 次,你不知道环长,不知道链长,甚至不知道 $n$
$n \leq 1000$
sol:
经典题...网上都是解法但我没搜
经典解法是这样的:
1.设置一个“快节点”,一个“慢节点”,快节点每次走两步,慢节点每次走一步
(具体到这道题就是第一步让他俩一起走,第二步只有快节点走)
2.这两个点如果相遇,说明链表有环
(这题一定有)
3.再把两个节点一个放回表头,另外一个在相遇点不动,把这两个点一起动,相遇的地方就是环的起始节点
(这题你相遇点有两个点,然后你惊喜的发现起始节点还有 8 个点没动,那就大家一起走就可以了)
4.输出 done,Pretest Passed,Accepted,然后大骂出题人
考验大家使用 Google / wiki 能力?
Stop Writing Problems
#include<bits/stdc++.h> #define LL long long #define rep(i,s,t) for(register int i = (s),i##end = (t); i <= i##end; ++i) #define dwn(i,s,t) for(register int i = (s),i##end = (t); i >= i##end; --i) using namespace std; inline int read() { int x=0,f=1;char ch; for(ch=getchar();!isdigit(ch);ch=getchar())if(ch=='-')f=-f; for(;isdigit(ch);ch=getchar())x=10*x+ch-'0'; return x*f; } const int maxn = 100010; string ch; int getres() { int x; cin >> x; rep(i, 1, x) cin >> ch; return x; } int main() { do { cout << "next 0 1" << endl; getres(); cout << "next 0" << endl; }while(getres() > 2); do { cout << "next 0 1 2 3 4 5 6 7 8 9" << endl; }while(getres() > 1); cout << "done" << endl; }