CERC 2017
A 模拟
#include <bits/stdc++.h> using namespace std; int r, n; char s[55][15]; int main() { scanf("%d%d", &r, &n); for (int i=1; i<=r+3; ++i) scanf("%s", s[i]); int cntl = 0, cntr = 0; for (int i=1; i<=r+3; ++i) { for (int j=0; j<=10; ++j) if (s[i][j]=='-') { if (j<=4) ++cntl; if (j>=6) ++cntr; } } char now = 'a'; while (n--) { int cnt1 = 0, cnt2 = 0; for (int i=0; i<=10; ++i) { if (s[2][i]=='-') ++cnt1; if (s[r/2+3][i]=='-') ++cnt2; } auto gao = [&](int x) { if (s[x][4]=='-'&&(s[x][6]!='-'||cntl>=cntr)) s[x][4]=now++,cntl--; else if (s[x][6]=='-') s[x][6]=now++,cntr--; else if (s[x][2]=='-'&&(s[x][8]!='-'||cntl>=cntr)) s[x][2]=now++,cntl--; else if (s[x][8]=='-') s[x][8]=now++,cntr--; else if (s[x][0]=='-'&&(s[x][10]!='-'||cntl>=cntr)) s[x][0]=now++,cntl--; else if (s[x][10]=='-') s[x][10]=now++,cntr--; else if (s[x][5]=='-') s[x][5]=now++; else if (s[x][1]=='-'&&(s[x][9]!='-'||cntl>=cntr)) s[x][1]=now++,cntl--; else s[x][9]=now++,cntr--; }; if (cnt1||cnt2) { if (cnt1>=cnt2) gao(2); else gao(r/2+3); } else { int cnt = -1, pos = 0; for (int i=1; i<=r+3; ++i) { int sum = 0; for (int j=0; j<=10; ++j) if (s[i][j]=='-') ++sum; if (sum>cnt) cnt = sum, pos = i; else if (sum==cnt) { if (min({abs(i-1),abs(i-r/2-2),abs(i-r-3)})<min({abs(pos-1),abs(pos-r/2-2),abs(pos-r-3)})) cnt = sum, pos = i; } } gao(pos); } } for (int i=1; i<=r+3; ++i) puts(s[i]); }
D
预处理出从每行第一列开始, 走$c$步回到哪一行, 这样找循环节可以$O(n+m)$求出移动$k$次位置
修改操作可以发现只会影响能到达$(x-1,y-1),(x,y-1),(x+1,y-1)$这三个点的行, 这是一段连续的区间, 可以$O(m)$维护
F
$nle 2p$时, $r$只能为$0$
$nle p$时, $r$不为$0$时必须修改位置$p$
$n<p$时, 枚举修改位置
#include <bits/stdc++.h> using namespace std; int qpow(long long a, int n, int p) { long long ans = 1; for (; n; a=a*a%p,n>>=1) if (n&1) ans=ans*a%p; return ans; } int main() { long long n; int p, r; scanf("%lld%d%d", &n, &p, &r); if (n>=2*p) { if (r) puts("-1 -1"); else puts("2 1"); return 0; } if (n>=p) { if (r==0) { if (p==2) { if (n==2) puts("-1 -1"); else puts("3 1"); } else puts("2 1"); } else { long long now = 1; for (int i=1; i<=n; ++i) if (i%p) now = now*i%p; for (int i=1; i<p; ++i) { if (now*i%p==r) return printf("%d %d ", p, i), 0; } puts("-1 -1"); } return 0; } long long now = 1; for (int i=1; i<=n; ++i) now = now*i%p; now = qpow(now,p-2,p); for (int k=2; k<=n; ++k) { int v = now*r%p*k%p; if (1<=v&&v<k) return printf("%d %d ", k, v), 0; } puts("-1 -1"); }
G
如果抽到比当前点期望小的点, 那么一定要走, 否则扔掉票
所以直接从$n$跑$dijkstra$即可
#include <bits/stdc++.h> using namespace std; const int N = 3e5+10; int n, m, deg[N], cnt[N]; bool vis[N]; double ans[N],sum[N]; vector<int> g[N]; int main() { scanf("%d%d", &n, &m); while (m--) { int u, v; scanf("%d%d", &u, &v); g[u].push_back(v); g[v].push_back(u); ++deg[u],++deg[v]; } for (int i=1; i<=n; ++i) ans[i] = 1e20; ans[n] = 0; priority_queue<pair<double,int>,vector<pair<double,int>>,greater<pair<double,int>>> q; q.push({0,n}); while (q.size()) { int x = q.top().second; q.pop(); if (vis[x]) continue; vis[x] = 1; for (int y:g[x]) if (!vis[y]) { ++cnt[y]; sum[y] += ans[x]; double w = (sum[y]+deg[y])/cnt[y]; q.push({ans[y]=w,y}); } } printf("%.20lf ", ans[1]); }
H
模拟
#include <bits/stdc++.h> using namespace std; const int N = 1e5+10; int n, tot, t, sz[N], fa[N]; char s[N]; vector<int> g[N]; string val[N],val2[N]; map<string,int> mp; int ID(string s) { if (mp.count(s)) return mp[s]; mp[s] = ++tot; val2[tot] = s; for (int i=s.size()-1; i>=0; --i) if (s[i]=='/') { val[tot] = s.substr(i+1,s.size()-1-i); break; } return tot; } void dfs(int x) { sort(g[x].begin(),g[x].end(),[](int a,int b){return val[a]<val[b];}); for (int y:g[x]) dfs(y),sz[x]+=sz[y]; } void dfs2(int x) { int cnt = 0, mx = 0; for (int y:g[x]) if (g[y].size()) { ++cnt; mx = max(mx, sz[y]); } if (!cnt) cout<<" "<<val2[x]<<"/ "<<sz[x]<<' '; else if (mx<t) cout<<"+ "<<val2[x]<<"/ "<<sz[x]<<' '; else { cout<<"- "<<val2[x]<<"/ "<<sz[x]<<' '; for (int y:g[x]) if (g[y].size()) dfs2(y); } } int main() { ID(""); scanf("%d", &n); while (n--) { int u; scanf("%s%d", s+1, &u); int len = strlen(s+1), pre = 1; for (int i=2; i<=len; ++i) { int j = i; while (j<=len&&s[j]!='/') ++j; int x = ID(string(s+1,s+j)); if (j>len) sz[x] = u; if (!fa[x]) fa[x] = pre, g[pre].push_back(x); pre = x, i = j; } } scanf("%d", &t); dfs(1); dfs2(1); }
I
对于位置$p_i,p_{i+1}$, 假设它们之间的数坐标范围在$[l,r]$, 那么就把$i$向$[l,r)$连边
那么一个询问的答案就是区间能到达的最小位置和最大位置
可以用倍增优化建图, 然后强连通分量缩点跑拓扑排序
#include <bits/stdc++.h> using namespace std; const int N = 1e5+10, LOG = 18; int n, m, tot, a[N], pos[N], Log[N], L2[N], R2[N]; int dfn[N*LOG], low[N*LOG], sccno[N*LOG]; int s[N*LOG], s_top, clk, scnt; int L[N*LOG],R[N*LOG],deg[N*LOG]; vector<int> g[N*LOG], f[N*LOG]; queue<int> q; struct { int mi[N][LOG], ma[N][LOG], id[N][LOG]; void init(int n, int *a) { for (int i=1; i<=n; ++i) { mi[i][0] = ma[i][0] = a[i]; id[i][0] = i; } for (int j=1; j<LOG; ++j) for (int i=1; i+(1<<j)-1<=n; ++i) { id[i][j] = ++tot; g[id[i][j]].push_back(id[i][j-1]); g[id[i][j]].push_back(id[i+(1<<j-1)][j-1]); mi[i][j] = min(mi[i][j-1], mi[i+(1<<j-1)][j-1]); ma[i][j] = max(ma[i][j-1], ma[i+(1<<j-1)][j-1]); } } void init(int n, int *L, int *R) { for (int i=1; i<=n; ++i) mi[i][0] = L[i], ma[i][0] = R[i]; for (int j=1; j<LOG; ++j) for (int i=1; i+(1<<j)-1<=n; ++i) { mi[i][j] = min(mi[i][j-1], mi[i+(1<<j-1)][j-1]); ma[i][j] = max(ma[i][j-1], ma[i+(1<<j-1)][j-1]); } } void add_edge(int x, int l, int r) { int t = Log[r-l+1]; r -= (1<<t)-1; g[x].push_back(id[l][t]); g[x].push_back(id[r][t]); } pair<int,int> rmq(int l, int r) { int t = Log[r-l+1]; r -= (1<<t)-1; return {min(mi[l][t],mi[r][t]),max(ma[l][t],ma[r][t])}; } } st; void dfs(int x) { dfn[s[++s_top]=x]=low[x]=++clk; for (int y:g[x]) { if (!dfn[y]) dfs(y),low[x]=min(low[x],low[y]); else if (!sccno[y]) low[x]=min(low[x],dfn[y]); } if (low[x]==dfn[x]) for(int u=++scnt; sccno[u=s[s_top--]]=scnt, u!=x; ); } int main() { scanf("%d", &n); for (int i=1; i<=n; ++i) scanf("%d", &a[i]), pos[a[i]] = i; Log[0] = -1; for (int i=1; i<=n; ++i) Log[i] = Log[i>>1]+1; tot = n; st.init(n, pos); for (int i=1; i<n; ++i) { int lx = a[i], rx = a[i+1], l, r; if (lx>rx) swap(lx, rx); tie(l,r) = st.rmq(lx, rx); if (l!=r-1) st.add_edge(i,l,r-1); } for (int i=1; i<=tot; ++i) { if (!dfn[i]) dfs(i); L[i] = n; } for (int i=1; i<=tot; ++i) { if (i<n) { L[sccno[i]] = min(L[sccno[i]], i); R[sccno[i]] = i; } for (int j:g[i]) if (sccno[i]!=sccno[j]) { f[sccno[j]].push_back(sccno[i]), ++deg[sccno[i]]; } } for (int i=1; i<=scnt; ++i) if (!deg[i]) q.push(i); while (q.size()) { int x = q.front(); q.pop(); for (int y:f[x]) { L[y] = min(L[y], L[x]); R[y] = max(R[y], R[x]); if (!--deg[y]) q.push(y); } } for (int i=1; i<n; ++i) L2[i] = L[sccno[i]], R2[i] = R[sccno[i]]; st.init(n-1,L2,R2); scanf("%d", &m); while (m--) { int x, y; scanf("%d%d", &x, &y); if (x==y) printf("%d %d ", x, y); else { int l, r; tie(l,r) = st.rmq(x, y-1); printf("%d %d ", l, r+1); } } }
J
枚举因子暴力dp
#include <bits/stdc++.h> using namespace std; const int N = 1e6+10; int n, ans[N], id[N], fa[N], sz[N]; vector<int> g[N]; void dfs(int x, int f) { for (int y:g[x]) if (y!=f) fa[y]=x,dfs(y,x); id[++*id] = x; } int solve(int k) { for (int i=1; i<=n; ++i) sz[i] = 1; for (int i=1; i<=n; ++i) { int x = id[i]; if (sz[x]>k) return 0; if (sz[x]!=k) sz[fa[x]] += sz[x]; } return !sz[0]; } int main() { scanf("%d", &n); for (int i=1; i<n; ++i) { int u, v; scanf("%d%d", &u, &v); g[u].push_back(v); g[v].push_back(u); } dfs(1,0); vector<int> ans; for (int i=n-1; i; --i) if (n%i==0&&solve(i)) printf("%d%c", n/i-1, " "[i==1]); }
K
I
斜着的正方形坐标变换一下 $x=x+y, y = x-y$, 然后二维前缀和统计
#include <bits/stdc++.h> using namespace std; const int M = 2510; int a[5100][5100],c[5100][5100]; char b[5100][5100]; void chkmax(int &a, int b) {a<b?a=b:0;} int main() { int n; scanf("%d", &n); while (n--) { char op; int x,y,z; scanf(" %c%d%d%d", &op, &x, &y, &z); if (op=='A') { z /= 2; x += M, y += M+1; ++c[x-z][y-z]; ++c[x+z][y+z]; --c[x-z][y+z]; --c[x+z][y-z]; } else { z /= 2; int tx = x+y, ty = x-y; x = tx, y = ty; x += M, y += M; ++a[x-z][y-z]; ++a[x+z][y+z]; --a[x-z][y+z]; --a[x+z][y-z]; } } int ans = 0; for (int i=0; i<5100; ++i) { for (int j=0; j<5100; ++j) { if (a[i][j]) { a[i+1][j] += a[i][j]; a[i][j+1] += a[i][j]; a[i+1][j+1] -= a[i][j]; int x = i-M, y = j-M; if ((x+y)%2==0) { int xx = (x+y)/2+M, yy = (x-y)/2+M; b[xx][yy] |= 1; b[xx][yy+1] |= 4; } else { ++y; int xx = (x+y)/2+M, yy = (x-y)/2+M; b[xx][yy+1] |= 8; b[xx-1][yy+1] |= 2; } } } } for (int i=0; i<5100; ++i) { for (int j=0; j<5100; ++j) { if (c[i][j]) { c[i+1][j] += c[i][j]; c[i][j+1] += c[i][j]; c[i+1][j+1] -= c[i][j]; ans += 4; } else { if (b[i][j]&1) ++ans; b[i][j] >>= 1; if (b[i][j]&1) ++ans; b[i][j] >>= 1; if (b[i][j]&1) ++ans; b[i][j] >>= 1; if (b[i][j]&1) ++ans; } } } printf("%.2lf ", ans/4.+1e-5); }
CERC 2019
A 哈希模拟
#include <bits/stdc++.h> const int N = 1e6+10; const int P1 = 876756319, B1 = 991; const int P2 = 799898821, B2 = 2333; int n, fac1[N], fac2[N]; int L1[N], L2[N], R1[N], R2[N]; char s[N]; void init() { for (int i=1; i<=n; ++i) { L1[i] = ((long long)L1[i-1]*B1+s[i]-'a'+1)%P1; L2[i] = ((long long)L2[i-1]*B2+s[i]-'a'+1)%P2; } for (int i=n; i; --i) { R1[i] = ((long long)R1[i+1]*B1+s[i]-'a'+1)%P1; R2[i] = ((long long)R2[i+1]*B2+s[i]-'a'+1)%P2; } } std::pair<int,int> HashL(int l, int r) { int x = (L1[r]-(long long)L1[l-1]*fac1[r-l+1])%P1; int y = (L2[r]-(long long)L2[l-1]*fac2[r-l+1])%P2; if (x<0) x+=P1; if (y<0) y+=P2; return std::pair<int,int>(x,y); } std::pair<int,int> HashR(int l, int r) { int x = (R1[l]-(long long)R1[r+1]*fac1[r-l+1])%P1; int y = (R2[l]-(long long)R2[r+1]*fac2[r-l+1])%P2; if (x<0) x+=P1; if (y<0) y+=P2; return std::pair<int,int>(x,y); } int main() { fac1[0] = fac2[0] = 1; for (int i=1; i<N; ++i) { fac1[i] = (long long)fac1[i-1]*B1%P1; fac2[i] = (long long)fac2[i-1]*B2%P2; } scanf("%d%s", &n, s+1); init(); if (L1[n]==R1[1]&&L2[n]==R2[1]) return puts("0"),0; for (int i=1; i<=n; ++i) { int x, y; std::tie(x, y) = HashR(1, i); int l1 = ((long long)L1[n]*fac1[i]+x)%P1; int l2 = ((long long)L2[n]*fac2[i]+y)%P2; std::tie(x, y) = HashL(1, i); int r1 = (R1[1]+(long long)x*fac1[n])%P1; int r2 = (R2[1]+(long long)y*fac2[n])%P2; if (l1==r1&&l2==r2) return printf("%d ", i), 0; } }
B
一个区间的gcd种类数是$O(log A)$的, 先预处理出前缀$gcd$和后缀$gcd$, 再处理出每个数作为最大值时的范围. 最后暴力合并即可, 复杂度是$O(nlog^3 A)$
#include <bits/stdc++.h> using namespace std; const int N = 111, P = 1e9+7; int n, a[N], pre[N], nxt[N]; vector<pair<int,int> > L[N], R[N]; int gcd(int x, int y) {return x?gcd(y%x,x):y;} int main() { scanf("%d", &n); for (int i=1; i<=n; ++i) scanf("%d", a+i); for (int i=1; i<=n; ++i) { auto v = L[i-1]; for (int j=0; j<v.size(); ++j) v[j].first = gcd(v[j].first, a[i]); v.push_back({a[i],i}); for (int j=0; j<v.size(); ) { int k = j; while (k<v.size()&&v[k].first==v[j].first) ++k; L[i].push_back(v[j]); j = k; } pre[i] = i-1; while (pre[i]&&a[pre[i]]<a[i]) pre[i]=pre[pre[i]]; } for (int i=n; i; --i) { auto v = R[i+1]; for (int j=0; j<v.size(); ++j) v[j].first = gcd(v[j].first, a[i]); v.push_back({a[i],i}); for (int j=0; j<v.size(); ) { int k = j; while (k<v.size()&&v[k].first==v[j].first) ++k; R[i].push_back(v[j]); j = k; } nxt[i] = i+1; while (nxt[i]<=n&&a[nxt[i]]<=a[i]) nxt[i]=nxt[nxt[i]]; } int ans = 0; for (int i=1; i<=n; ++i) { int l = pre[i]+1, r = nxt[i]-1; for (int x=0; x<L[i].size(); ++x) for (int y=0; y<R[i].size(); ++y) { int l1 = L[i][x].second, r1 = x+1==L[i].size()?i:L[i][x+1].second-1; int l2 = y+1==R[i].size()?i:R[i][y+1].second+1, r2 = R[i][y].second; if (r1<l||l2>r) continue; l1 = max(l1, l), r2 = min(r2, r); ans = (ans+(long long)gcd(L[i][x].first,R[i][y].first)*a[i]%P*(r1-l1+1)%P*(r2-l2+1))%P; } } printf("%d ", ans); }
C 非叶节点贡献是度数-2
#include <bits/stdc++.h> using namespace std; const int N = 3e5+10; int main() { int n; scanf("%d", &n); vector<int> deg(n+1); for (int i=1; i<n; ++i) { int u, v; scanf("%d%d", &u, &v); ++deg[u], ++deg[v]; } int ans = 0; for (int i=1; i<=n; ++i) if (deg[i]>1) ans += deg[i]-2; printf("%d ", ans); }
D
E 每个点做圆得到与直线相交的线段, 离散化求出被线段覆盖最多的点即可
#include <bits/stdc++.h> using namespace std; const int N = 3e5+10; double x[N], y[N]; int main() { int n; double r, a, b; scanf("%d%lf%lf%lf", &n, &r, &a, &b); for (int i=1; i<=n; ++i) scanf("%lf%lf", x+i, y+i); if (!b) { swap(a, b); for (int i=1; i<=n; ++i) swap(x[i], y[i]); } vector<pair<double,int> > s; for (int i=1; i<=n; ++i) { double A = a*a+b*b; double B = a*b*x[i]+b*b*y[i]; double C = b*b*(x[i]*x[i]+y[i]*y[i]-r*r); double D = B*B-A*C; if (D<-1e-8) continue; D = sqrt(D); s.push_back({(B-D)/A,-1}); s.push_back({(B+D)/A,1}); } sort(s.begin(),s.end()); int ans = 0, now = 0; for (auto &t:s) ans = max(ans, now-=t.second); printf("%d ", ans); }
F 分块
#include <bits/stdc++.h> using namespace std; const int N = 3e5+10; int main() { long long n, m, ans = 0; cin>>n>>m; auto calc = [](long long n) { long long ans = 0; for (long long i=1,j; i<=n; i=j+1) { j = n/(n/i); ans += (j-i+1)*(n/i); } return ans; }; cout<<calc(m)-calc(n-1)<<endl; }
G 从高位到低位贪心
#include <bits/stdc++.h> using namespace std; const int N = 2e5+10; int n, k, a[N]; int main() { scanf("%d%d", &n, &k); for (int i=1; i<=n; ++i) scanf("%d", a+i); int ans = 0; for (int i=30; i>=0; --i) { int cnt = 0; for (int j=1; j<=n; ++j) if (a[j]>>i&1) ++cnt; if (cnt>=k) { cnt = 0; for (int j=1; j<=n; ++j) if (a[j]>>i&1) a[++cnt] = a[j]; n = cnt; ans ^= 1<<i; } } printf("%d ", ans); }
H AC自动机矩阵快速幂
#include <bits/stdc++.h> using namespace std; const int N = 111, P = 1e9+7; int n, q, cnt; char s[N]; struct AC_automaton { int rt, ch[N][26], fa[N], val[N]; void init() { rt = cnt = 1; memset(ch[1],0,sizeof ch[1]); fa[1] = 0; } int newnode() { ++cnt; memset(ch[cnt],0,sizeof ch[cnt]); fa[cnt] = 0; return cnt; } void ins(char *s) { int now = rt, n = strlen(s); for (int i=0; i<n; ++i) { int c = s[i]-'a'; if (!ch[now][c]) ch[now][c]=newnode(); now = ch[now][c]; } val[now] = 1; } void build() { queue<int> q; for (int i=0; i<26; ++i) { if (ch[rt][i]) fa[ch[rt][i]]=rt,q.push(ch[rt][i]); else ch[rt][i]=rt; } while (q.size()) { int u = q.front(); q.pop(); val[u] |= val[fa[u]]; for (int i=0; i<26; ++i) { int &v = ch[u][i]; if (v) { fa[v] = ch[fa[u]][i]; q.push(v); } else v = ch[fa[u]][i]; } } } } ac; struct Mat { int v[N][N]; Mat() {memset(v, 0, sizeof v);} Mat operator * (const Mat& b) const { Mat c; for (int k=1; k<=cnt; ++k) for (int i=1; i<=cnt; ++i) for (int j=1; j<=cnt; ++j) c.v[i][j] = ((long long)v[i][k]*b.v[k][j]+c.v[i][j])%P; return c; } Mat operator ^ (int nn) { Mat b, a=*this; for (int i=1; i<=cnt; ++i) b.v[i][i] = 1; while (nn) { if(nn&1) b=b*a; nn>>=1,a=a*a; } return b; } } g; int main() { scanf("%d%d", &n, &q); ac.init(); while (q--) { scanf("%*d%s", s); ac.ins(s); } ac.build(); for (int i=1; i<=cnt; ++i) { for (int j=0; j<26; ++j) { if (!ac.val[ac.ch[i][j]]) ++g.v[i][ac.ch[i][j]]; } } g = g^n; int ans = 0; for (int i=1; i<=cnt; ++i) ans = (ans+g.v[1][i])%P; printf("%d ", ans); }
I 优先交换$2$元环, 然后$3$元环, 然后$4$元环
#include <bits/stdc++.h> using namespace std; const int N = 1e6+10; int n,f[500][500]; char a[N],b[N],s[]={'A','C','G','T'}; int main() { scanf("%s%s", a+1, b+1); n = strlen(a+1); for (int i=1; i<=n; ++i) if (a[i]!=b[i]) ++f[a[i]][b[i]]; int ans = 0, t; auto gao = [&](char x, char y) { t = min(f[x][y],f[y][x]); ans += t, f[x][y] -= t, f[y][x] -= t; }; auto gao2 = [&](char x, char y, char z) { t = min({f[x][y],f[y][z],f[z][x]}); ans += 2*t, f[x][y] -= t, f[y][z] -= t, f[z][x] -= t; }; auto gao3 = [&](char x, char y, char z, char w) { t = min({f[x][y],f[y][z],f[z][w],f[w][x]}); ans += 3*t, f[x][y] -= t, f[y][z] -= t, f[z][w] -= t, f[w][x] -= t; }; for (auto x:s) for (auto y:s) gao(x,y); for (auto x:s) for (auto y:s) for (auto z:s) gao2(x,y,z); for (auto x:s) for (auto y:s) for (auto z:s) for (auto w:s) gao3(x,y,z,w); printf("%d ", ans); }
J 按度数分块即可
#include <bits/stdc++.h> using namespace std; const int N = 1e5+10, S = 400; int n, e, p, fa[N], a[N], u[N], v[N], deg[N], vis[N]; vector<int> g[N]; int Find(int x) {return fa[x]?fa[x]=Find(fa[x]):x;} int main() { scanf("%d%d%d", &n, &e, &p); for (int i=1; i<=e; ++i) { scanf("%d%d", u+i, v+i); ++deg[u[i]], ++deg[v[i]]; } for (int i=1; i<=e; ++i) { if (deg[u[i]]<=S) g[u[i]].push_back(v[i]); if (deg[v[i]]<=S) g[v[i]].push_back(u[i]); if (deg[u[i]]>S&°[v[i]]>S) { g[u[i]].push_back(v[i]); g[v[i]].push_back(u[i]); } } while (p--) { int m; scanf("%d", &m); for (int i=1; i<=m; ++i) scanf("%d", a+i), vis[a[i]] = 1; int cnt = m; for (int i=1; i<=m; ++i) { int u = Find(a[i]); for (auto &t:g[a[i]]) if (vis[t]) { int v = Find(t); if (u!=v) fa[v]=u,--cnt; } } for (int i=1; i<=m; ++i) fa[a[i]] = vis[a[i]] = 0; printf("%d ", cnt); } }
K
L 分类讨论
#include <bits/stdc++.h> using namespace std; const int N = 2e5+10; int n, a[N], b[N], vis[N], Log[N], mi[N][19], ma[N][19]; vector<int> g[N]; void init() { Log[0] = -1; for (int i=1; i<=n; ++i) mi[i][0] = ma[i][0] = a[i], Log[i]=Log[i>>1]+1; for (int j=1; j<=Log[n]; ++j) for (int i=1;i+(1<<j)-1<=n; ++i) { mi[i][j] = min(mi[i][j-1], mi[i+(1<<j-1)][j-1]); ma[i][j] = max(ma[i][j-1], ma[i+(1<<j-1)][j-1]); } } pair<int,int> rmq(int l, int r) { int t = Log[r-l+1]; r -= (1<<t)-1; return {min(mi[l][t],mi[r][t]),max(ma[l][t],ma[r][t])}; } struct { int c[N]; void add(int x) { for (; x<=*b; x+=x&-x) ++c[x]; } int query(int l, int r) { int ans = 0; for (int x=l-1; x; x^=x&-x) ans -= c[x]; for (int x=r; x; x^=x&-x) ans += c[x]; return ans; } } bit; int main() { scanf("%d", &n); for (int i=1; i<=n; ++i) scanf("%d", a+i), b[i] = a[i]; sort(b+1, b+1+n); *b = unique(b+1, b+1+n)-b-1; for (int i=1; i<=n; ++i) { a[i] = lower_bound(b+1, b+1+*b, a[i])-b; g[a[i]].push_back(i); } init(); for (int i=1; i<=*b; ++i) { if (g[i].size()>=3) vis[111] = 1; if (g[i].size()>=2) { int l, r; tie(l, r) = rmq(g[i][0], g[i].back()); if (l<i) vis[212] = 1; if (r>i) vis[121] = 1; tie(l, r) = rmq(g[i][1], n); if (l<i) vis[221] = 1; if (r>i) vis[112] = 1; tie(l, r) = rmq(1, g[i][g[i].size()-2]); if (l<i) vis[122] = 1; if (r>i) vis[211] = 1; } } int x=-1, xx=-1; for (int i=1; i<=n; ++i) { if (a[i]<xx) vis[321] = 1; if (a[i]<x) xx = max(xx, a[i]); x = max(x, a[i]); } x = xx = n+1; for (int i=1; i<=n; ++i) { if (a[i]>xx) vis[123] = 1; if (a[i]>x) xx = min(xx, a[i]); x = min(x, a[i]); } for (int i=1; i<=n; ++i) { if (1<i&&i<n) { int l, r; tie(l, r) = rmq(i+1, n); if (r>a[i]+1&&bit.query(a[i]+1,r-1)) vis[213] = 1; if (l<a[i]-1&&bit.query(l+1,a[i]-1)) vis[231] = 1; } bit.add(a[i]); } memset(bit.c,0,sizeof bit.c); for (int i=n; i; --i) { if (1<i&&i<n) { int l, r; tie(l, r) = rmq(1, i-1); if (r>a[i]+1&&bit.query(a[i]+1,r-1)) vis[312] = 1; if (l<a[i]-1&&bit.query(l+1,a[i]-1)) vis[132] = 1; } bit.add(a[i]); } for (int i=111; i<=333; ++i) if (vis[i]) printf("%d ", i); }
CERC 2018
A ac自动机处理一下每个位置最长匹配长度, 然后dp的时候要支持区间取最小值, 单点求最值, 直接线段树, 因为区间和单点询问都是单调的, 也可以线性做
#include <bits/stdc++.h> using namespace std; const int N = 3e5+10; struct AC_automaton { int rt, cnt, ch[N][26], fa[N], val[N]; void init() { rt = cnt = 1; memset(ch[1],0,sizeof ch[1]); fa[1] = 0; } int newnode() { ++cnt; memset(ch[cnt],0,sizeof ch[cnt]); fa[cnt] = 0; return cnt; } void ins(char *s) { int now = rt, n = strlen(s); for (int i=0; i<n; ++i) { int c = s[i]-'a'; if (!ch[now][c]) ch[now][c]=newnode(); now = ch[now][c]; } val[now] = n; } void build() { queue<int> q; for (int i=0; i<26; ++i) { if (ch[rt][i]) fa[ch[rt][i]]=rt,q.push(ch[rt][i]); else ch[rt][i]=rt; } while (q.size()) { int u = q.front(); q.pop(); val[u] = max(val[u], val[fa[u]]); for (int i=0; i<26; ++i) { int &v = ch[u][i]; if (v) { fa[v] = ch[fa[u]][i]; q.push(v); } else v = ch[fa[u]][i]; } } } } ac; struct seg { int mi[N<<2]; void build(int o, int l, int r) { mi[o] = 1e9; int mid = (l+r)/2; if (l!=r) build(o<<1,l,mid),build(o<<1|1,mid+1,r); } void upd(int o, int l, int r, int ql, int qr, int v) { if (ql<=l&&r<=qr) { mi[o] = min(mi[o], v); return; } int mid = (l+r)/2; if (mid>=ql) upd(o<<1,l,mid,ql,qr,v); if (mid<qr) upd(o<<1|1,mid+1,r,ql,qr,v); } void query(int o, int l, int r, int x, int &v) { v = min(v, mi[o]); if (l==r) return; int mid = (l+r)/2; if (mid>=x) query(o<<1,l,mid,x,v); else query(o<<1|1,mid+1,r,x,v); } } tr; int n, L, vis[N]; char s[N],a[N]; int main() { scanf("%d%s", &L, s); ac.init(); for (int i=1; i<=L; ++i) { scanf("%s", a); ac.ins(a); } ac.build(); int now = ac.rt, n = strlen(s); for (int i=0; i<n; ++i) { now = ac.ch[now][s[i]-'a']; vis[i] = ac.val[now]; } tr.build(1,0,n); tr.upd(1,0,n,n,n,0); for (int i=n-1; i>=0; --i) { int dp = 1e9; tr.query(1,0,n,i+1,dp); if (dp==1e9) return puts("-1"), 0; if (vis[i]) tr.upd(1,0,n,i-vis[i]+1,i,dp+1); } int ans = 1e9; tr.query(1,0,n,0,ans); if (ans==1e9) puts("-1"); else printf("%d ", ans); }
B 建$10$个图, 第$i$个图只连权值$<i$的边, 询问只要暴力枚举答案, 判断连通性即可, 可以离线lct或者线段树分治
C 迭代加深搜索
#include <bits/stdc++.h> using namespace std; char s[50]; int ans, n; long long mx; int dfs(long long x, int d) { if (x==mx) return 1; if (!d) return 0; for (int i=1; i<=n; ++i) { long long y = x|(x>>i); if (y>x&&dfs(y,d-1)) return 1; } return 0; } int main() { scanf("%s", s); n = strlen(s); long long x = 0; for (int i=0; i<n; ++i) { x = x*2+s[i]-'0'; mx = mx*2+1; } if (s[0]=='0') return puts("-1"),0; for (int i=0; i<=n; ++i) { if (dfs(x,i)) return printf("%d ", i), 0; } }
D
E
F
G
H
I 暴力
#include <bits/stdc++.h> using namespace std; const int N = 1e6+5; int f[N]; int main() { for (int i=1; i<N; ++i) for (int j=(N+i-1)/i-1; j>=i+1; --j) for (int k=(N+i*j-1)/(i*j)-1; k>=j+1; --k) ++f[i*j*k]; for (int i=1; i<N; ++i) f[i] += f[i-1]; int t; scanf("%d", &t); while (t--) { int n; scanf("%d", &n); printf("%d ", f[n]); } }
J 分四个方向dp一下
#include <bits/stdc++.h> using namespace std; const int N = 1e3+10; int n, m, dp[N][N], len[N][N]; char s[N][N]; long long ans; void solve() { for (int i=0; i<n; ++i) for (int j=0; j<m; ++j) { if (j&&s[i][j]==s[i][j-1]) len[i][j] = len[i][j-1]+1; else len[i][j] = 1; } for (int i=n-1; i>=0; --i) for (int j=0; j<m; ++j) { if (s[i][j]==s[i+1][j]) dp[i][j] = max(1, dp[i+1][j]-1); else dp[i][j] = 1; while (dp[i][j]<i+1&&j+dp[i][j]<m&&s[i-dp[i][j]][j]==s[i][j]&&len[i-dp[i][j]][j+dp[i][j]]>dp[i][j]) ++dp[i][j]; ans += dp[i][j]-1; } } int main() { scanf("%d%d", &n, &m); for (int i=0; i<n; ++i) scanf("%s", s[i]); solve(); for (int i=0; i<n; ++i) reverse(s[i], s[i]+m); solve(); for (int j=0; j<m; ++j) for (int i=0; i<n-i-1; ++i) swap(s[i][j],s[n-i-1][j]); solve(); for (int i=0; i<n; ++i) reverse(s[i], s[i]+m); solve(); printf("%lld ", ans); }
K
L
当$A=B$时, 是平等博弈, 可以得到$x$个石子的$sg$值是$x space modspace (A+1)$
当$A>B$时, 可以看成参数为$B$的博弈
如果存在一堆$xge B+1$, 那么先手取$B+1$个石子, 这样可以让$sg$值异或和不变, 先手必胜
如果每一堆石子个数都$<B+1$, 转化为平等博弈
当$A<B$时, 可以看成参数为$A$的博弈
如果$sg$值异或和为$0$, 先手必败
否则先手第一步操作必须删掉$ge A+1$的堆并且让$sg$为$0$才能赢, 否则必败
#include <bits/stdc++.h> using namespace std; int n, a, b, x[100010]; int main() { scanf("%d%d%d", &n, &a, &b); int sg = 0, mi = min(a, b); for (int i=1; i<=n; ++i) scanf("%d", x+i), sg ^= x[i]%(mi+1); if (a==b) return puts(sg?"Petyr":"Varys"),0; if (a>b) { if (sg) puts("Petyr"); else { for (int i=1; i<=n; ++i) if (x[i]>=mi+1) { return puts("Petyr"), 0; } puts("Varys"); } return 0; } int p = 0; for (int i=1; i<=n; ++i) if (x[i]>=mi+1) { if (p) return puts("Varys"), 0; p = i; } if (!p) return puts(sg?"Petyr":"Varys"),0; sg ^= x[p]%(a+1); for (int i=x[p]-a; i<=a; ++i) if (sg==(x[p]-i)%(a+1)) return puts("Petyr"), 0; puts("Varys"); }
2018 icpc Yokohama
A 模拟
#include <bits/stdc++.h> using namespace std; const int N = 2e5+10; int n; char s[N],t[N]; int chk(char *a, char *b) { int n = strlen(a+1), m = strlen(b+1); for (int i=1; i<=n&&i<=m; ++i) { if (isdigit(a[i])&&isdigit(b[i])) { int x = i, y = i; while (x<n&&isdigit(a[x+1])) ++x; while (y<m&&isdigit(b[y+1])) ++y; if (x!=y) return x<y; for (int j=i; j<=x; ++j) if (a[j]!=b[j]) return a[j]<b[j]; i = x; } else if (a[i]!=b[i]) { if (isdigit(a[i])!=isdigit(b[i])) return isdigit(a[i]); return a[i]<b[i]; } } return n<m; } int main() { scanf("%d%s", &n, s+1); while (n--) { scanf("%s", t+1); if (chk(t,s)) puts("-"); else puts("+"); } }
B 暴力枚举最后两项, 二分找前面项
#include <bits/stdc++.h> using namespace std; const int N = 5010; int n, a[N], f[N][N]; int main() { scanf("%d", &n); for (int i=1; i<=n; ++i) scanf("%d", a+i); sort(a+1,a+1+n); for (int i=1; i<=n; ++i) for (int j=i+1; j<=n; ++j) f[j][i] = 2; int ans = 2; for (int i=1; i<=n; ++i) for (int j=2; j<i; ++j) { int x = lower_bound(a+1,a+j,2*a[j]-a[i])-a; if (a[x]==2*a[j]-a[i]) { f[i][j] = max(f[i][j], f[j][x]+1); ans = max(ans, f[i][j]); } } printf("%d ", ans); }
C
D 求字典序最小, 那么就考虑从前往后贪心, 那么需要求出每个后缀的答案检验是否合法
设${dp}_{i,j}$表示只考虑第一个串后缀$i$和第二个串后缀$j$时的最小值, 可以用序列自动机$O(1)$转移
#include <bits/stdc++.h> using namespace std; const int N = 4e3+10; int n, m, pre1[N][2], pre2[N][2], nxt1[N][2], nxt2[N][2], dp[N][N]; int len[N][N]; char s[N],t[N]; void chkmin(int &a, int b) {a>b?a=b:0;} int main() { scanf("%s%s", s+1, t+1); n = strlen(s+1); m = strlen(t+1); nxt1[n+1][0]=nxt1[n+1][1]=n+1; nxt2[m+1][0]=nxt2[m+1][1]=m+1; for (int i=n; i; --i) { nxt1[i][0] = nxt1[i+1][0]; nxt1[i][1] = nxt1[i+1][1]; nxt1[i][s[i]-'0'] = i; } for (int i=m; i; --i) { nxt2[i][0] = nxt2[i+1][0]; nxt2[i][1] = nxt2[i+1][1]; nxt2[i][t[i]-'0'] = i; } memset(len,0x3f,sizeof len); for (int i=n+1; i; --i) for (int j=m+1; j; --j) for (int k=0; k<=1; ++k) { int ni = nxt1[i][k], nj = nxt2[j][k]; if (ni==n+1&&nj==m+1) chkmin(len[i][j], 1); else chkmin(len[i][j], len[min(n+1,ni+1)][min(nj+1,m+1)]+1); } int x = 0, y = 0, ans = len[1][1]; for (int i=1; i<=ans; ++i) { int tx = nxt1[min(x+1,n+1)][0], ty = nxt2[min(y+1,m+1)][0]; if (i==ans) { if (tx<=n||ty<=m) putchar('1'); else putchar('0'); } else { if (len[min(n+1,tx+1)][min(m+1,ty+1)]<=ans-i) putchar('0'),x=tx,y=ty; else putchar('1'),x=nxt1[min(x+1,n+1)][1],y=nxt2[min(y+1,m+1)][1]; } } puts(""); }
E
F
G
考虑每个数的贡献, 要么加入左边递增部分, 贡献是左侧比它大的数的个数, 要么加入右边递减部分, 贡献是右侧比它大的数的个数, 两者取最小即可
#include <bits/stdc++.h> using namespace std; const int N = 1e5+10; int n, a[N], c[N], f[N]; int main() { scanf("%d", &n); for (int i=1; i<=n; ++i) scanf("%d", a+i); for (int i=1; i<=n; ++i) { for (int x=a[i]+1; x<N; x+=x&-x) f[i] += c[x]; for (int x=a[i]; x; x-=x&-x) ++c[x]; } memset(c,0,sizeof c); long long ans = 0; for (int i=n; i; --i) { int sum = 0; for (int x=a[i]+1; x<N; x+=x&-x) sum += c[x]; for (int x=a[i]; x; x-=x&-x) ++c[x]; ans += min(f[i], sum); } printf("%lld ", ans); }
H
I
J
每种颜色按$dfs$排序, 用一个$set$维护, 修改颜色看做删点添点, 讨论一下相对其他点位置关系即可
#include <bits/stdc++.h> using namespace std; const int N = 1e5+10; int n, clk, a[N], dep[N], sz[N], son[N], fa[N]; int top[N], ans[N], L[N], R[N], id[N]; vector<int> g[N]; struct cmp { bool operator () (int x, int y) const {return L[x]<L[y];} }; set<int,cmp> s[N]; void dfs(int x, int f, int d) { fa[x] = f, dep[x] = d, sz[x] = 1; L[x] = ++clk, id[clk] = x; for (int y:g[x]) if (y!=f) { dfs(y,x,d+1); sz[x] += sz[y]; if (sz[y]>sz[son[x]]) son[x]=y; } R[x] = clk; } void dfs(int x, int tf) { top[x] = tf; if (son[x]) dfs(son[x],tf); for (int y:g[x]) if (!top[y]) dfs(y,y); } int lca(int x, int y) { while (top[x]!=top[y]) { if (dep[top[x]]<dep[top[y]]) y=fa[top[y]]; else x=fa[top[x]]; } return dep[x]<dep[y]?x:y; } int calc(int x, int v) { if (s[v].empty()) return 0; int l = *s[v].begin(), r = *s[v].rbegin(); if (L[l]<L[x]||L[r]>R[x]) return 0; return dep[lca(l,r)]-dep[x]; } int gao(int x, int v) { auto p = s[v].lower_bound(x); int l=-1, r=-1; if (p!=s[v].end()) r = *p; if (p!=s[v].begin()) l = *prev(p); int y = -1; if (l!=-1) y = lca(l, x); if (r!=-1) { int t = lca(r, x); if (y==-1||dep[t]>dep[y]) y = t; } if (y==-1) return calc(x,v); return dep[x]-dep[y]+calc(y,v); } void add(int x, int v) { ans[v] += gao(x, v); s[v].insert(x); } void del(int x, int v) { s[v].erase(x); ans[v] -= gao(x, v); } int main() { scanf("%d", &n); for (int i=1; i<n; ++i) { int u, v; scanf("%d%d", &u, &v); g[u].push_back(v); g[v].push_back(u); } dfs(1,0,0),dfs(1,1); for (int i=1; i<=n; ++i) scanf("%d", a+i), add(i, a[i]); int q; scanf("%d", &q); while (q--) { char c; int x, y; scanf(" %c%d", &c, &x); if (c=='Q') { if (s[x].empty()) puts("-1"); else printf("%d ", ans[x]); } else { scanf("%d", &y); del(x,a[x]); a[x] = y; add(x,a[x]); } } }
K
CERC 2015
A 模拟
#include <bits/stdc++.h> using namespace std; string s[7]; char tmp[111]; int gao(int x) { if (s[0].substr(x,5)=="xxxxx"&&s[3].substr(x,5)=="x...x") return 0; if (s[0].substr(x,5)=="....x"&&s[1].substr(x,5)=="....x") return 1; if (s[1].substr(x,5)=="....x"&&s[4].substr(x,5)=="x....") return 2; if (s[1].substr(x,5)=="....x"&&s[3].substr(x,5)=="xxxxx") return 3; if (s[0].substr(x,5)=="x...x") return 4; if (s[1].substr(x,5)=="x...."&&s[4].substr(x,5)=="....x") return 5; if (s[1].substr(x,5)=="x...."&&s[4].substr(x,5)=="x...x") return 6; if (s[3].substr(x,5)=="....x") return 7; if (s[4].substr(x,5)=="x...x") return 8; if (s[0].substr(x,5)=="xxxxx") return 9; return -1; } void print(int x, int v) { if (v==0) { s[0] = s[0]+"xxxxx"; s[1] = s[1]+"x...x"; s[2] = s[2]+"x...x"; s[3] = s[3]+"x...x"; s[4] = s[4]+"x...x"; s[5] = s[5]+"x...x"; s[6] = s[6]+"xxxxx"; } else if (v==1) { for (int i=0; i<7; ++i) s[i] = s[i]+"....x"; } else if (v==2) { s[0] = s[0]+"xxxxx"; s[1] = s[1]+"....x"; s[2] = s[2]+"....x"; s[3] = s[3]+"xxxxx"; s[4] = s[4]+"x...."; s[5] = s[5]+"x...."; s[6] = s[6]+"xxxxx"; } else if (v==3) { s[0] = s[0]+"xxxxx"; s[1] = s[1]+"....x"; s[2] = s[2]+"....x"; s[3] = s[3]+"xxxxx"; s[4] = s[4]+"....x"; s[5] = s[5]+"....x"; s[6] = s[6]+"xxxxx"; } else if (v==4) { s[0] = s[0]+"x...x"; s[1] = s[1]+"x...x"; s[2] = s[2]+"x...x"; s[3] = s[3]+"xxxxx"; s[4] = s[4]+"....x"; s[5] = s[5]+"....x"; s[6] = s[6]+"....x"; } else if (v==5) { s[0] = s[0]+"xxxxx"; s[1] = s[1]+"x...."; s[2] = s[2]+"x...."; s[3] = s[3]+"xxxxx"; s[4] = s[4]+"....x"; s[5] = s[5]+"....x"; s[6] = s[6]+"xxxxx"; } else if (v==6) { s[0] = s[0]+"xxxxx"; s[1] = s[1]+"x...."; s[2] = s[2]+"x...."; s[3] = s[3]+"xxxxx"; s[4] = s[4]+"x...x"; s[5] = s[5]+"x...x"; s[6] = s[6]+"xxxxx"; } else if (v==7) { s[0] = s[0]+"xxxxx"; s[1] = s[1]+"....x"; s[2] = s[2]+"....x"; s[3] = s[3]+"....x"; s[4] = s[4]+"....x"; s[5] = s[5]+"....x"; s[6] = s[6]+"....x"; } else if (v==8) { s[0] = s[0]+"xxxxx"; s[1] = s[1]+"x...x"; s[2] = s[2]+"x...x"; s[3] = s[3]+"xxxxx"; s[4] = s[4]+"x...x"; s[5] = s[5]+"x...x"; s[6] = s[6]+"xxxxx"; } else if (v==9) { s[0] = s[0]+"xxxxx"; s[1] = s[1]+"x...x"; s[2] = s[2]+"x...x"; s[3] = s[3]+"xxxxx"; s[4] = s[4]+"....x"; s[5] = s[5]+"....x"; s[6] = s[6]+"xxxxx"; } } int main() { for (int i=0; i<7; ++i) cin>>s[i]; int x = 0, y = 0, f = 0; for (int i=0; i<s[0].size(); i+=6) { int t = gao(i); if (0<=t&&t<=9) { if (f) y = y*10+t; else x = x*10+t; } else f = 1; } int cnt = 0, ans = x+y; while (ans) tmp[++cnt] = ans%10, ans /= 10; for (int i=0; i<7; ++i) s[i].clear(); int now = 0; for (int i=cnt; i; --i) { print(now,tmp[i]); if (i>1) for (int i=0; i<7; ++i) s[i] += "."; now += 6; } for (int i=0; i<7; ++i) cout<<s[i]<<' '; }
B 二分答案
#include <bits/stdc++.h> using namespace std; const int N = 1e6+10; int n,a,b,sum[N]; string s[N],t; int len(int i, int j) { return sum[j]-sum[i-1]+j-i; } int main() { getline(cin,t); stringstream ss(t); while (ss>>t) s[++n]=t; cin>>a>>b; for (int i=1; i<=n; ++i) sum[i]=sum[i-1]+s[i].size(); for (int x=a; x<=b; ++x) { int t = 1, ans = 0; while (t<=n) { int l=t,r=n,pos=-1; while (l<=r) { int mid = (l+r)/2; if (len(t,mid)<=x) pos=mid,l=mid+1; else r=mid-1; } if (ans) ans += s[t].size()+1; else ans += s[t].size(); t = pos+1; } printf("%d ",ans); } }
C
D 求一下前缀模$m$为$0$的位置个数即可
#include <bits/stdc++.h> using namespace std; int n, m; char s[300010]; int main() { scanf("%d%d%s", &n, &m, s+1); int num = 0, ans = 1; for (int i=1; i<=n; ++i) { num = (num*10+s[i]-'0')%m; if (!num&&i!=n) ans = ans*2ll%1000000007; } if (num) ans = 0; printf("%d ", ans); }
E 从大到小添边, 考虑添加一条边$(u,v)$, 相当于要把$u$和$v$度数$+1$, 可以独立考虑
如果$u$度数$>2$, 那么不会有影响
如果$u$度数$=2$, 那么边数$+1$, 点数$+1$
如果$u$度数$=1$, 那么边数$-1$, 点数$-1$
如果$u$度数$=0$, 那么边数不变, 点数$+1$
但这样会把一个大环算成$0$条边, $0$个点, 再用并查集统计大环的个数
#include <bits/stdc++.h> using namespace std; const int N = 3e5+10; int n, m, ans1[N], ans2[N], deg[N], fa[N], vis[N], cnt[N], sz[N], iscycle[N]; struct _ {int u,v,w;} e[N]; int Find(int x) {return fa[x]?fa[x]=Find(fa[x]):x;} int main() { scanf("%d%d", &n, &m); for (int i=1; i<=m; ++i) scanf("%d%d%d", &e[i].u, &e[i].v, &e[i].w); sort(e+1, e+1+m, [](_ a,_ b){return a.w>b.w;}); int num1 = 0, num2 = 0, cycle = 0; auto add = [&](int x) { if (deg[x]==0) ++num1; if (deg[x]==1) --num1,--num2; if (deg[x]==2) ++num1,++num2; ++deg[x]; }; for (int i=1; i<=n; ++i) sz[i] = 1; for (int i=1; i<=m; ++i) { add(e[i].u); add(e[i].v); int u = Find(e[i].u), v = Find(e[i].v); if (u!=v) { if (iscycle[u]) iscycle[u] = 0, --cycle; if (iscycle[v]) iscycle[v] = 0, --cycle; fa[u] = v; sz[v] += sz[u]; cnt[v] += cnt[u]+1; vis[v] |= vis[u]; if (deg[e[i].u]>2||deg[e[i].v]>2) vis[v] = 1; } else { if (iscycle[u]) iscycle[u] = 0, --cycle; if (deg[e[i].u]>2||deg[e[i].v]>2) vis[u] = 1; ++cnt[u]; //点数等于边数且度数均不超过2的连通块一定为大环 if (!vis[u]&&sz[u]==cnt[u]) iscycle[u] = 1, ++cycle; } ans1[i] = num1+cycle, ans2[i] = ++num2+cycle; } int q; scanf("%d", &q); while (q--) { int x; scanf("%d", &x); int l=1, r=m, p=0; while (l<=r) { int mid = (l+r)/2; if (e[mid].w>=x) p=mid,l=mid+1; else r=mid-1; } printf("%d %d ", ans1[p], ans2[p]); } }
F 设$g_{i,j}=f_{i,j}+frac{c}{a+b-1}$, $g_{i,j}=a g_{i,j-1}+b g_{i-1,j}$, 转化为格路径计数
#include <bits/stdc++.h> using namespace std; const int N = 2e5+10, P = 1e6+3; int l[N],t[N],fac[N*2],ifac[N*2]; int qpow(long long a, int n) { long long ans = 1; for (; n; n>>=1,a=a*a%P) if (n&1) ans = ans*a%P; return ans; } int C(int n, int m) { return (long long)fac[n]*ifac[m]%P*ifac[n-m]%P; } int main() { int n, a, b, c; scanf("%d%d%d%d", &n, &a, &b, &c); int k = c*(long long)qpow(a+b-1,P-2)%P; fac[0] = 1; for (int i=1; i<=2*n; ++i) fac[i] = (long long)fac[i-1]*i%P; ifac[2*n] = qpow(fac[2*n], P-2); for (int i=2*n-1; i>=0; --i) ifac[i] = ifac[i+1]*(i+1ll)%P; for (int i=1; i<=n; ++i) scanf("%d", l+i), l[i] = (l[i]+k)%P; for (int i=1; i<=n; ++i) scanf("%d", t+i), t[i] = (t[i]+k)%P; int ans = -k; for (int i=2; i<=n; ++i) { ans = (ans+t[i]*(long long)qpow(b,n-1)%P*qpow(a,n-i)%P*C(n-i+n-2,n-2))%P; ans = (ans+l[i]*(long long)qpow(a,n-1)%P*qpow(b,n-i)%P*C(n-i+n-2,n-2))%P; } if (ans<0) ans += P; printf("%d ", ans); }
G
H
I
J
最大流转化为最小割, 两点不连通那么最小割=0, 不在一个边双内, 那么最小割=1, 如果删除任意一条边, 两点都在一个边双, 那么最小割=3, 否则为2
可以枚举删边, 跑tarjan, hash判断边双是否相同
K 相当于给了一个基环内向树森林, 先拆掉非环上的点, 最后处理环即可
#include <bits/stdc++.h> using namespace std; const int N = 2e5+10; int n, deg[N], nxt[N], vis[N], vis2[N]; int main() { scanf("%d", &n); for (int i=1; i<=2*n; ++i) scanf("%d", &nxt[i]), ++deg[nxt[i]]; vector<int> ans; for (int i=1; i<=2*n; ++i) if (!deg[i]&&!vis[i]) { int cur = i; while (!deg[cur]) { if (!vis[cur]) ans.push_back(cur), vis[cur] = vis[nxt[cur]] = 1; --deg[nxt[cur]]; cur = nxt[cur]; } } for (int i=1; i<=2*n; ++i) if (deg[i]&&!vis2[i]) { int cur = i, pos = i; while (!vis2[cur]) { if (vis[cur]) pos = cur; vis2[cur] = 1; cur = nxt[cur]; } cur = pos; do { if (!vis[cur]) ans.push_back(cur),vis[cur]=vis[nxt[cur]]=1; cur = nxt[cur]; } while (cur!=pos); } for (int i=0; i<ans.size(); ++i) printf("%d%c", ans[i], " "[i+1==ans.size()]); }
L
2018 icpc Dhaka
A
B 裸数位$dp$, 逆序对可以枚举两位, 贡献就是其他位选取方案, 复杂度可以优化到$O(10 n)$
#include <bits/stdc++.h> using namespace std; int64_t sum[20], pw[20]; int a[20],cnt[10]; int64_t calc(int64_t x) { for (int i=0; i<10; ++i) cnt[i] = 0; int n = 0; while (x) a[++n] = x%10, x /= 10; int64_t ans = sum[n-1], now = 0; for (int i=n; i; --i) { int l = i==n, r = a[i]-1; if (l<=r) { if (i>=3) ans += (r-l+1)*45*(i-1)*(i-2)/2*pw[i-3]; int u = 0; for (int x=0; x<r; ++x) u += cnt[x], ans += u*pw[i-1]; ans += now*(r-l+1)*pw[i-1]; if (i>=2) { for (int x=0; x<9; ++x) ans += (r-l+1)*(9-x)*(i-1)*cnt[x]*pw[i-2]; for (int x=l; x<=r; ++x) ans += (9-x)*(i-1)*pw[i-2]; } } for (int x=0; x<a[i]; ++x) now += cnt[x]; ++cnt[a[i]]; } return ans+now; } int main() { pw[0] = 1; for (int i=1; i<20; ++i) pw[i] = pw[i-1]*10; for (int i=2; i<20; ++i) { sum[i] = sum[i-1]+36*(i-1)*pw[i-2]; if (i>2) sum[i] += 405*(i-1)*(i-2)/2*pw[i-3]; } int t; scanf("%d", &t); for (int clk=1; clk<=t; ++clk) { int64_t l, r; scanf("%lld%lld", &l, &r); int64_t ans = calc(r); if (l>1) ans -= calc(l-1); printf("Case %d: %lld ", clk, ans); } }
C 显然是积性函数, 统计下每个素因子贡献即可
#include <bits/stdc++.h> using namespace std; const int N = 1e6+10, P = 1e7+7; int cnt, p[N], vis[N]; int main() { for (int i=2; i*i<N; ++i) if (!vis[i]) { for (int j=i*i; j<N; j+=i) vis[j] = 1; } for (int i=2; i<N; ++i) if (!vis[i]) p[++cnt] = i; for (int n; scanf("%d", &n), n; ) { int ans = 1; for (int i=1; p[i]<=n; ++i) { int k = 1, x = n; while (x/=p[i]) k = (k+x)%P; ans = k*(k+1ll)/2%P*ans%P; } printf("%d ", ans); } }
D
E 模拟
F 路径并板子
G 相当于每次询问给定点$x$,区间$[l,r]$, 求$x$到区间$[l,r]$内点的最小距离
考虑分块, 预处理块内的点到整棵树所有点的最短距离, 这个直接用个队列bfs就行
块边缘的点直接$rmq$查询距离即可, 复杂度$O(nsqrt{n})$
H
可以发现一直上下左右旋转就可以得到所有状态, 每个状态能分成若干个环, 求出环长lcm即可
#include <bits/stdc++.h> using namespace std; typedef vector<vector<int>> node; const int N = 210, P = 78294349; char s[N][N]; int n, m, X[N*N], Y[N*N], vis[N][N]; int ma[N*N], f[N*N]; node rotup(node u) { node v = u; for (int i=0; i<m; ++i) { int cnt = 0; for (int j=0; j<n; ++j) if (u[j][i]) v[cnt++][i] = u[j][i]; while (cnt!=n) v[cnt++][i] = 0; } return v; } node rotdown(node u) { node v = u; for (int i=0; i<m; ++i) { int cnt = n-1; for (int j=n-1; j>=0; --j) if (u[j][i]) v[cnt--][i] = u[j][i]; while (cnt>=0) v[cnt--][i] = 0; } return v; } node rotleft(node u) { node v = u; for (int i=0; i<n; ++i) { int cnt = 0; for (int j=0; j<m; ++j) if (u[i][j]) v[i][cnt++] = u[i][j]; while (cnt!=m) v[i][cnt++] = 0; } return v; } node rotright(node u) { node v = u; for (int i=0; i<n; ++i) { int cnt = m-1; for (int j=m-1; j>=0; --j) if (u[i][j]) v[i][cnt--] = u[i][j]; while (cnt>=0) v[i][cnt--] = 0; } return v; } int main() { for (int i=2; i<N*N; ++i) if (!ma[i]) { for (int j=i; j<N*N; j+=i) ma[j] = i; } int t; scanf("%d", &t); for (int clk=1; clk<=t; ++clk) { scanf("%d%d", &n, &m); node a(n,vector<int>(m)); for (int i=0; i<n; ++i) scanf("%s", s[i]); int tot = 0; for (int i=0; i<n; ++i) { for (int j=0; j<m; ++j) { if (s[i][j]=='.') a[i][j] = 0; else { a[i][j] = ++tot; X[tot] = i; Y[tot] = j; } } } auto b = rotleft(rotdown(rotright(rotup(a)))); for (int i=0; i<n; ++i) for (int j=0; j<m; ++j) vis[i][j] = 0; for (int i=1; i<=n*m; ++i) f[i] = 0; for (int i=0; i<n; ++i) { for (int j=0; j<m; ++j) if (!vis[i][j]) { int curx = i, cury = j, cnt = 0; while (!vis[curx][cury]) { vis[curx][cury] = 1; ++cnt; int nxtx = X[b[curx][cury]], nxty = Y[b[curx][cury]]; curx = nxtx, cury = nxty; } while (ma[cnt]) { int t = 0, d = ma[cnt]; while (cnt%d==0) cnt /= d, ++t; f[d] = max(f[d], t); } } } int ans = 1; for (int i=1; i<=n*m; ++i) while (f[i]--) ans = (int64_t)ans*i%P; printf("Case %d: %d ", clk, ans); } }
I
2020 ICPC Universidad Nacional de Colombia Programming Contest
A 简单几何题, 不会
B SA板子
#include <bits/stdc++.h> using namespace std; const int N = 2e5+10; struct SA { int n,c[N],rk[N],h[N],sa[N],a[N]; void build(int m) { a[n+1] = rk[n+1] = h[n+1] = 0; int i,*x=rk,*y=h; for(i=1;i<=m;i++) c[i]=0; for(i=1;i<=n;i++) c[x[i]=a[i]]++; for(i=1;i<=m;i++) c[i]+=c[i-1]; for(i=n;i;i--) sa[c[x[i]]--]=i; for(int k=1,p;k<=n;k<<=1) { p=0; for(i=n-k+1;i<=n;i++) y[++p]=i; for(i=1;i<=n;i++) if(sa[i]>k) y[++p]=sa[i]-k; for(i=1;i<=m;i++) c[i]=0; for(i=1;i<=n;i++) c[x[y[i]]]++; for(i=1;i<=m;i++) c[i]+=c[i-1]; for(i=n;i;i--) sa[c[x[y[i]]]--]=y[i]; swap(x,y); x[sa[1]]=1; p=1; for(i=2;i<=n;i++) x[sa[i]]=(y[sa[i-1]]==y[sa[i]] && y[sa[i-1]+k]==y[sa[i]+k])?p:++p; if(p==n) break; m=p; } for(i=1;i<=n;i++) rk[sa[i]]=i; for(int i=1,j,k=0;i<=n;i++) if (rk[i]!=1) { if(k) k--; j=sa[rk[i]-1]; while(a[i+k]==a[j+k]) k++; h[rk[i]] = k; } } } sa1, sa2; char s[N],t[N]; int main() { scanf("%s%s", s+1, t+1); int n = strlen(s+1), m = strlen(t+1); for (int i=1; i<=n; ++i) sa1.a[i] = s[i]-'a'+1; for (int i=1; i<=m; ++i) sa2.a[i] = t[i]-'a'+1; sa1.n = n, sa2.n = m; sa1.build(200), sa2.build(200); for (int i=sa1.sa[n]; i<=n; ++i) { if (i==sa1.sa[n]||s[i]>=t[sa2.sa[m]]) putchar(s[i]); else break; } for (int i=sa2.sa[m]; i<=m; ++i) putchar(t[i]); puts(""); }
C 设长$n$的答案为$f_n$, 可以得到$f_n=a^n-sumlimits_{d|n,d ot =n} f_d$
#include <bits/stdc++.h> using namespace std; const int P = 1e9+7; int dp[5000010]; int main() { int a,k; scanf("%d%d", &a, &k); int b = a, ans = 0; for (int i=1; i<=k; ++i) { dp[i] = (dp[i]+b)%P; for (int j=2*i; j<=k; j+=i) dp[j] = (dp[j]-dp[i])%P; b = (int64_t)b*a%P; ans = (ans+dp[i])%P; } if (ans<0) ans += P; printf("%d ", ans); }
D 就是求一个多项式$n$次幂, 快速幂优化一下
#include <bits/stdc++.h> using namespace std; const int P = 1e9+7; int n, m, k, a[222], f[222], tmp[222]; int qpow(int64_t a, int n) { int64_t ans = 1; for (; n; a=a*a%P,n>>=1) if (n&1) ans=ans*a%P; return ans; } int main() { scanf("%d%d%d", &n, &k, &m); int cnt = 0; for (int i=1; i<=k; ++i) if (i%m) ++cnt, ++a[i%m]; for (int i=1; i<m; ++i) a[i] = (int64_t)a[i]*qpow(cnt,P-2)%P; f[0] = 1; for (; n; n>>=1) { if (n&1) { for (int x=0; x<m; ++x) tmp[x] = 0; for (int x=0; x<m; ++x) for (int y=0; y<m; ++y) tmp[(x+y)%m] = (tmp[(x+y)%m]+(int64_t)f[x]*a[y])%P; for (int x=0; x<m; ++x) f[x] = tmp[x]; } for (int x=0; x<m; ++x) tmp[x] = 0; for (int x=0; x<m; ++x) for (int y=0; y<m; ++y) tmp[(x+y)%m] = (tmp[(x+y)%m]+(int64_t)a[x]*a[y])%P; for (int x=0; x<m; ++x) a[x] = tmp[x]; } printf("%d ", f[0]); }
E 简单交互, 从根节点往下问就行, 问到最后一层直接输出
#include <bits/stdc++.h> int main() { int n; scanf("%d", &n); auto ask = [&](int x) { if (!n--) { printf("! %d ", x); fflush(stdout); exit(0); } printf("%d ", x); fflush(stdout); int t; scanf("%d", &t); if (!t) { printf("! %d ", x); fflush(stdout); exit(0); } return t; }; int dep = ask(1), now = 1, d = dep-1; while (1) { int t = ask(2*now); if (t==d) now *= 2, --d; else now = now*2+1, --d; if (d<0) ask(now); } }
F 分层图最短路
#include <bits/stdc++.h> using namespace std; const int INF = 1e9, N = 1e4+10; int n, m, a, b, k; vector<pair<int,int>> g[N],f[N]; vector<vector<int>> dij(vector<pair<int,int>> g[], int x) { struct Q { int id,y,w; bool operator < (const Q &rhs) const { return w>rhs.w; } }; vector<vector<int>> d(k+1,vector<int>(n,INF)); vector<vector<int>> vis(k+1,vector<int>(n)); priority_queue<Q> q; q.push({x,0,d[0][x]=0}); while (q.size()) { Q u = q.top(); q.pop(); if (vis[u.y][u.id]) continue; vis[u.y][u.id] = 1; for (auto &t:g[u.id]) { int v = t.first, w = d[u.y][u.id]+t.second; if (w<d[u.y][v]) q.push({v,u.y,d[u.y][v]=w}); if (u.y==k) continue; w = d[u.y][u.id]; if (w<d[u.y+1][v]) q.push({v,u.y+1,d[u.y+1][v]=w}); } } return d; } int main() { scanf("%d%d%d%d%d", &n, &m, &a, &b, &k); while (m--) { int u, v, w; scanf("%d%d%d", &u, &v, &w); g[u].push_back({v,w}); f[v].push_back({u,w}); } auto ga = dij(g,a), gb = dij(g,b); auto fa = dij(f,a), fb = dij(f,b); int ans = INF, pos = -1; for (int i=0; i<n; ++i) if (i!=a&&i!=b) for (int x=0; x<=k; ++x) for (int y=0; x+y<=k; ++y) for (int z=0; z<=k; ++z) for (int w=0; z+w<=k; ++w) if (ga[x][i]+(int64_t)gb[z][i]+fa[y][i]+fb[w][i]<ans) ans = ga[x][i]+gb[z][i]+fa[y][i]+fb[w][i], pos = i; if (pos==-1) puts(">:("); else printf("%d %d ", pos, ans); }
G 题目保证$m$个对不交叉, 所以答案只跟$n,m$有关
#include <bits/stdc++.h> using namespace std; const int P = 1e9+7; int main() { int n, m; scanf("%d%d", &n, &m); int ans = 1; for (int i=1; i<=n; ++i) ans = (int64_t)ans*i%P; for (int i=1; i<=m; ++i) ans = ans*(P+1ll)/2%P; printf("%d ", ans); }
H 回文树板子, 就是求下本质不同奇回文个数
#include <bits/stdc++.h> using namespace std; const int N = 1e5+10; struct PalindromicTree { int tot,cnt,last,len[N],fa[N],ch[N][26]; char s[N]; void init() { s[0] = '#', last = 0, cnt = 1, tot = 0; fa[0] = 1, len[0] = 0, len[1] = -1; memset(ch[0],0,sizeof ch[0]); memset(ch[1],0,sizeof ch[0]); } int newnode() { ++cnt; memset(ch[cnt],0,sizeof ch[0]); fa[cnt] = len[cnt] = 0; return cnt; } int getfail(int x) { while (s[tot-len[x]-1]!=s[tot]) x=fa[x]; return x; } void ins(int c) { s[++tot] = c; int p = getfail(last); if (!ch[p][c]) { int q = newnode(); len[q] = len[p]+2; fa[q] = ch[getfail(fa[p])][c]; ch[p][c] = q; } last = ch[p][c]; } } pam; char s[N]; int main() { int n; scanf("%d%s", &n, s+1); pam.init(); for (int i=1; i<=n; ++i) pam.ins(s[i]-'a'); int ans = 0; for (int i=1; i<=pam.cnt; ++i) if (pam.len[i]%2&&pam.len[i]>1) ++ans; printf("%d ", ans); }
I 单调栈处理一下左侧右侧能到达的最近的点, 然后按权值从大到小$dp$
#include <bits/stdc++.h> using namespace std; const int N = 1e5+10; int L[N], R[N]; pair<int,int> a[N]; int64_t dp[N]; int main() { int n; scanf("%d", &n); for (int i=1; i<=n; ++i) scanf("%d", &a[i].first), a[i].second=i; vector<int> sta; for (int i=1; i<=n; ++i) { while (sta.size()&&a[i].first>a[sta.back()].first) sta.pop_back(); if (sta.size()&&a[i].first==a[sta.back()].first) L[i] = L[sta.back()]; else { if (sta.size()) L[i] = sta.back(); sta.push_back(i); } } sta.clear(); for (int i=n; i; --i) { while (sta.size()&&a[i].first>a[sta.back()].first) sta.pop_back(); if (sta.size()&&a[i].first==a[sta.back()].first) R[i] = R[sta.back()]; else { if (sta.size()) R[i] = sta.back(); sta.push_back(i); } } sort(a+1, a+1+n, greater<pair<int,int>>()); for (int i=1; i<=n; ++i) { int id = a[i].second; if (L[id]) dp[id] = max(dp[id], dp[L[id]]+id-L[id]); if (R[id]) dp[id] = max(dp[id], dp[R[id]]+R[id]-id); } for (int i=1; i<=n; ++i) printf("%lld ", dp[i]);puts(""); }
J
K 模拟
#include <bits/stdc++.h> using namespace std; const int N = 1e5+10; char A[N], B[N]; int main() { scanf("%s%s", A+1, B+1); int n = strlen(A+1); int m = strlen(B+1); int now = 1; for (int i=1; i<=n; ++i) { if (isdigit(A[i])) { int j = i; while (j<n&&isdigit(A[j+1])) ++j; int nxt = now; while (nxt<m&&isdigit(B[nxt+1])) ++nxt; if (nxt-now!=j-i) return puts(j-i<nxt-now?"<":">"),0; for (int k=i; k<=j; ++k) { if (A[k]!=B[now]) return puts(A[k]<B[now]?"<":">"),0; ++now; } i = j; } else { if (A[i]!=B[now]) return puts(A[i]<B[now]?"<":">"),0; ++now; } } puts("="); }
L 预处理上下左右隧道走到的位置, 从终点$bfs$一下, 然后从起点贪心求字典序最小的最短路
#include <bits/stdc++.h> using namespace std; const int N = 2e3+10; const int dx[]={0,0,-1,1}; const int dy[]={-1,1,0,0}; int n, m, L[N][N], R[N][N], U[N][N], D[N][N], dis[N][N]; char s[N][N]; int main() { scanf("%d%d", &n, &m); for (int i=0; i<n; ++i) scanf("%s", s[i]); queue<pair<int,int>> q; int x, y; for (int i=0; i<n; ++i) { for (int j=0; j<m; ++j) { L[i][j] = R[i][j] = U[i][j] = D[i][j] = dis[i][j] = -1; if (s[i][j]=='E') q.push({i,j}), dis[i][j] = 0; } } for (int i=0; i<n; ++i) { for (int j=0; j<m; ++j) if (s[i][j]!='X') { if (j+1<m&&s[i][j+1]=='X') { int k = j+1; while (k+1<m&&s[i][k+1]=='X') ++k; if (k+1<m) R[i][j] = k+1; } if (j-1>=0&&s[i][j-1]=='X') { int k = j-1; while (k-1>=0&&s[i][k-1]=='X') --k; if (k-1>=0) L[i][j] = k-1; } if (i+1<n&&s[i+1][j]=='X') { int k = i+1; while (k+1<n&&s[k+1][j]=='X') ++k; if (k+1<n) D[i][j] = k+1; } if (i-1>=0&&s[i-1][j]=='X') { int k = i-1; while (k-1>=0&&s[k-1][j]=='X') --k; if (k-1>=0) U[i][j] = k-1; } } } while (q.size()) { auto u = q.front(); q.pop(); if (U[u.first][u.second]!=-1&&dis[U[u.first][u.second]][u.second]==-1) { dis[U[u.first][u.second]][u.second] = dis[u.first][u.second]+1; q.push({U[u.first][u.second],u.second}); } if (D[u.first][u.second]!=-1&&dis[D[u.first][u.second]][u.second]==-1) { dis[D[u.first][u.second]][u.second] = dis[u.first][u.second]+1; q.push({D[u.first][u.second],u.second}); } if (L[u.first][u.second]!=-1&&dis[u.first][L[u.first][u.second]]==-1) { dis[u.first][L[u.first][u.second]] = dis[u.first][u.second]+1; q.push({u.first,L[u.first][u.second]}); } if (R[u.first][u.second]!=-1&&dis[u.first][R[u.first][u.second]]==-1) { dis[u.first][R[u.first][u.second]] = dis[u.first][u.second]+1; q.push({u.first,R[u.first][u.second]}); } for (int k=0; k<4; ++k) { pair<int,int> v(u.first+dx[k],u.second+dy[k]); if (v.first<0||v.second<0||v.first>=n||v.second>=m) continue; if (s[v.first][v.second]!='X'&&dis[v.first][v.second]==-1) { dis[v.first][v.second] = dis[u.first][u.second]+1; q.push(v); } } } int ans; for (int i=0; i<n; ++i) for (int j=0; j<m; ++j) if (s[i][j]=='S') ans = dis[i][j], x = i, y = j; if (ans==-1) return puts("-1"), 0; printf("%d ", ans); vector<pair<int,int>> g[2]; g[0].push_back({x,y}); for (int i=0; i<ans; ++i) { g[i&1^1].clear(); for (auto [x,y]:g[i&1]) { int ok = 0; if (x+1<n&&dis[x+1][y]==dis[x][y]-1) { ok = 1; g[i&1^1].push_back({x+1,y}); } if (D[x][y]!=-1&&dis[D[x][y]][y]==dis[x][y]-1) { ok = 1; g[i&1^1].push_back({D[x][y],y}); } if (ok) { putchar('D'); continue; } if (y>0&&dis[x][y-1]==dis[x][y]-1) { ok = 1; g[i&1^1].push_back({x,y-1}); } if (L[x][y]!=-1&&dis[x][L[x][y]]==dis[x][y]-1) { ok = 1; g[i&1^1].push_back({x,L[x][y]}); } if (ok) { putchar('L'); continue; } if (y+1<m&&dis[x][y+1]==dis[x][y]-1) { ok = 1; g[i&1^1].push_back({x,y+1}); } if (R[x][y]!=-1&&dis[x][R[x][y]]==dis[x][y]-1) { g[i&1^1].push_back({x,R[x][y]}); ok = 1; } if (ok) { putchar('R'); continue; } if (x>0&&dis[x-1][y]==dis[x][y]-1) { g[i&1^1].push_back({x-1,y}); ok = 1; } if (U[x][y]!=-1&&dis[U[x][y]][y]==dis[x][y]-1) { g[i&1^1].push_back({U[x][y],y}); ok = 1; } if (!ok) throw; putchar('U'); } } puts(""); }
M 序列自动机板子
#include <bits/stdc++.h> const int N = 2e5+10; int nxt[N][26]; char s[N]; int main() { scanf("%s", s+1); int n = strlen(s+1); for (int i=0; i<26; ++i) nxt[n+1][i]=nxt[n+2][i]=n+1; for (int i=n; i; --i) { memcpy(nxt[i],nxt[i+1],sizeof nxt[i]); nxt[i][s[i]-'a'] = i; } int q; scanf("%d", &q); while (q--) { scanf("%s", s+1); int m = strlen(s+1), now = 1; for (int i=1; i<=m; ++i) { now = nxt[now][s[i]-'a']+1; if (now>n+1) { if (i==1) puts("IMPOSSIBLE"); else s[i] = 0, puts(s+1); break; } if (i==m) puts(s+1); } } }
N
2016-2017 National Taiwan University World Final Team Selection Contest
A 离线二分答案, 转化为对于一个$01$序列进行排序, 用线段树模拟即可
#include <bits/stdc++.h> using namespace std; const int N = 1e5+10; int n, m, a[N], b[N], c[N], l[N], r[N]; struct { int sum,tag; void upd(int v, int len) { sum = v*len; tag = v; } } tr[N<<2]; void build(int o, int l, int r) { tr[o].tag = -1; if (l==r) tr[o].sum = c[l]; else { int mid = (l+r)/2; build(o<<1,l,mid),build(o<<1|1,mid+1,r); tr[o].sum = tr[o<<1].sum+tr[o<<1|1].sum; } } void pd(int o, int l, int r) { if (tr[o].tag!=-1) { int mid = (l+r)/2; tr[o<<1].upd(tr[o].tag,mid-l+1); tr[o<<1|1].upd(tr[o].tag,r-mid); tr[o].tag=-1; } } int query(int o, int l, int r, int ql, int qr) { if (ql<=l&&r<=qr) return tr[o].sum; pd(o,l,r); int ans = 0, mid = (l+r)/2; if (mid>=ql) ans += query(o<<1,l,mid,ql,qr); if (mid<qr) ans += query(o<<1|1,mid+1,r,ql,qr); return ans; } void upd(int o, int l, int r, int ql, int qr, int v) { if (ql<=l&&r<=qr) return tr[o].upd(v,r-l+1); pd(o,l,r); int mid = (l+r)/2; if (mid>=ql) upd(o<<1,l,mid,ql,qr,v); if (mid<qr) upd(o<<1|1,mid+1,r,ql,qr,v); tr[o].sum = tr[o<<1].sum+tr[o<<1|1].sum; } int chk(int x) { for (int i=1; i<=n; ++i) c[i] = a[i]>=x; build(1,1,n); for (int i=1; i<=m; ++i) { if (l[i]<=r[i]) { int sum = query(1,1,n,l[i],r[i]); if (sum&&sum!=r[i]-l[i]+1) { upd(1,1,n,r[i]-sum+1,r[i],1); upd(1,1,n,l[i],r[i]-sum,0); } } else { int sum = query(1,1,n,r[i],l[i]); if (sum&&sum!=l[i]-r[i]+1) { upd(1,1,n,r[i],r[i]+sum-1,1); upd(1,1,n,r[i]+sum,l[i],0); } } } return query(1,1,n,(1+n)/2,(1+n)/2); } int main() { scanf("%d%d", &n, &m); for (int i=1; i<=n; ++i) scanf("%d", a+i), b[i] = a[i]; for (int i=1; i<=m; ++i) scanf("%d%d",l+i,r+i); sort(b+1,b+1+n); int lx = 1, rx = n, ans; while (lx<=rx) { int mid = (lx+rx)/2; if (chk(b[mid])) ans=mid,lx=mid+1; else rx=mid-1; } printf("%d ", b[ans]); }
B
C 一个格子合法等价于边界的交点数等于$2$, 并且合法的格子一定是八连通的, 直接$bfs$模拟
#include <bits/stdc++.h> using namespace std; const int N = 2e3+10; const int dx[]={0,0,-1,-1,-1,1,1,1}; const int dy[]={1,-1,0,-1,1,0,-1,1}; int n, clk, a[N][N], vis[N][N]; int chk(int x, int y, int x1, int y1, int x2, int y2) { int L = min(x1, x2)+1, R = max(x1, x2); int D = min(y1, y2)+1, U = max(y1, y2); if (x<L||x>R||y<D||y>U) return 0; if (vis[x][y]==clk) return 0; if (y1<y2) swap(x1,x2),swap(y1,y2); int t = (x1-x2)*(y-y1)+x1*(y1-y2), cnt = 0; if ((x-1)*(y1-y2)<=t&&t<x*(y1-y2)) ++cnt; t = (x1-x2)*(y-1-y1)+x1*(y1-y2); if ((x-1)*(y1-y2)<t&&t<=x*(y1-y2)) ++cnt; if (x1<x2) swap(x1,x2),swap(y1,y2); t = (y1-y2)*(x-x1)+y1*(x1-x2); if ((y-1)*(x1-x2)<t&&t<=y*(x1-x2)) ++cnt; t = (y1-y2)*(x-1-x1)+y1*(x1-x2); if ((y-1)*(x1-x2)<=t&&t<y*(x1-x2)) ++cnt; return cnt>=2; } int main() { scanf("%d", &n); while (n--) { int x1,y1,x2,y2; scanf("%d%d%d%d", &x1, &y1, &x2, &y2); if (x1==x2||y1==y2) continue; queue<pair<int,int> > q; ++clk; for (int k=0; k<8; ++k) { if (chk(x1+dx[k],y1+dy[k],x1,y1,x2,y2)) { q.push({x1+dx[k],y1+dy[k]}); vis[x1+dx[k]][y1+dy[k]] = clk; } } while (q.size()) { auto u = q.front(); q.pop(); for (int k=0; k<8; ++k) { pair<int,int> v(u.first+dx[k],u.second+dy[k]); if (chk(v.first,v.second,x1,y1,x2,y2)) { q.push(v); vis[v.first][v.second] = clk; } } } } int sum = 0; for (int i=1; i<=2000; ++i) for (int j=1; j<=2000; ++j) if (vis[i][j]) ++sum; printf("%d ", sum); }
D
E
F
G 二分答案, 考虑$bfs$判断和不超过$x$的个数是否超过$k$
先排序, 每个状态存当前和,以及选取的最大数, 每次往后暴力遍历, 和超过$x$就退出, 这样每次检验复杂度就是$O(k)$的
#include <bits/stdc++.h> using namespace std; const int N = 2e5+10; int n, k, a[N]; int chk(long long x) { struct Q {long long s;int id;}; queue<Q> q; q.push({0,1}); int cnt = 0; while (q.size()) { auto u = q.front(); q.pop(); for (int i=u.id; i<=n; ++i) { if (u.s+a[i]<=x) { if (++cnt==k) return 1; q.push({u.s+a[i],i+1}); } else break; } } return 0; } int main() { scanf("%d%d", &n, &k); for (int i=1; i<=n; ++i) scanf("%d", a+i); sort(a+1,a+1+n); long long l=0, r=1e18, ans; while (l<=r) { long long mid = (l+r)/2; if (chk(mid)) ans=mid,r=mid-1; else l=mid+1; } printf("%lld ", ans); }
H
I
J
最优解一定是选取一个区间, 然后把区间内$1$全移走, 还有剩余操作数的话, 再把区间外的$0$移进来
所以一个区间$[l,r]$的贡献就为$sum(l,r,0)+k-sum(l,r,1)$, 并且要求满足$sum(l,r,1)le k$, 枚举右端点, 单调队列求出最优左端点即可
#include <bits/stdc++.h> using namespace std; const int N = 1e6+10; char s[N]; int n, q, sum[N][2]; void chkmax(int &a, int b) {a<b?a=b:0;} void work() { int k; scanf("%d", &k); deque<int> q; q.push_back(0); int ans = k; for (int i=1; i<=n; ++i) { while (q.size()&&sum[q[0]][1]<sum[i][1]-k) q.pop_front(); if (q.size()) ans = max(ans, sum[i][0]-sum[q[0]][0]+k-sum[i][1]+sum[q[0]][1]); while (q.size()&&sum[i][1]-sum[i][0]>=sum[q.back()][1]-sum[q.back()][0]) q.pop_back(); q.push_back(i); } ans = min(ans, sum[n][0]); printf("%d ", ans); } int main() { scanf("%s%d", s+1, &q); n = strlen(s+1); for (int i=1; i<=n; ++i) { sum[i][0] = sum[i-1][0]; sum[i][1] = sum[i-1][1]; ++sum[i][s[i]=='1']; } while (q--) work(); }
2019 icpc Jakarta
A 简单签到
B
直接暴力树形$dp$, 设${dp}_{0,0}$表示没和儿子匹配方案, ${dp}_{0,1}$表示没和儿子匹配,但之前有可以匹配的没匹配的方案, ${dp}_{1,0}$表示和一个儿子匹配的方案, ${dp}_{1,1}$表示和一个儿子配,但之前有可以匹配没匹配的方案,${dp}_{2,0}$表示与两个儿子匹配的方案
枚举所有情况转移即可, 假设与儿子匹配, 那么就从${dp}_{y,0,0},{dp}_{y,1,0},{dp}_{y,1,1}$三种状态转移, 假设不与儿子匹配, 那么就从${dp}_{y,0,0},{dp}_{y,1,0},{dp}_{y,2,0}$三种状态转移, 一共有$5 imes 6=30$种转移方式, 还要注意${dp}_{x,2,0}$不能与儿子匹配, 那么也就是$27$种转移. 写完要数一下27种, 保证不重不漏
#include <bits/stdc++.h> using namespace std; const int N = 1e5+10, P = 1e9+7; int n, dp[N][3][2]; vector<int> g[N]; void dfs(int x, int f) { dp[x][0][0] = 1; for (int y:g[x]) if (y!=f) { dfs(y,x); int sum = (dp[y][0][0]+dp[y][1][0]+(int64_t)dp[y][2][0])%P; int add = (dp[y][0][0]+dp[y][1][0]+(int64_t)dp[y][1][1])%P; int nxt00 = (int64_t)dp[x][0][0]*dp[y][2][0]%P; int nxt01 = ((int64_t)dp[x][0][0]*dp[y][0][0]+(int64_t)dp[x][0][0]*dp[y][1][0]+(int64_t)dp[x][0][1]*sum)%P; int nxt10 = ((int64_t)dp[x][1][0]*dp[y][2][0]+(int64_t)dp[x][0][0]*add)%P; int nxt11 = ((int64_t)dp[x][1][0]*dp[y][0][0]+(int64_t)dp[x][1][0]*dp[y][1][0]+(int64_t)dp[x][1][1]*sum+(int64_t)dp[x][0][1]*add)%P; int nxt20 = ((int64_t)dp[x][1][0]*add+(int64_t)dp[x][1][1]*add+(int64_t)dp[x][2][0]*sum)%P; dp[x][0][0] = nxt00; dp[x][0][1] = nxt01; dp[x][1][0] = nxt10; dp[x][1][1] = nxt11; dp[x][2][0] = nxt20; } } int main() { scanf("%d", &n); for (int i=1; i<n; ++i) { int u, v; scanf("%d%d", &u, &v); g[u].push_back(v); g[v].push_back(u); } dfs(1,0); printf("%lld ", (dp[1][0][0]+dp[1][1][0]+(int64_t)dp[1][2][0])%P); }
C 只要起点终点为偶数, 并且中间行和中间列的奇偶性不变, 那么起点终点对应的一块矩形就全是偶数
#include <bits/stdc++.h> using namespace std; int main() { int n, q; scanf("%d%d", &n, &q); vector<int> R(n),C(n); for (int i=0; i<n; ++i) scanf("%d", &R[i]), R[i] &= 1; for (int i=0; i<n; ++i) scanf("%d", &C[i]), C[i] &= 1; vector<int> f(n), g(n); for (int i=0; i<n; ++i) { int j = i; while (j+1<n&&R[j+1]==R[i]) ++j; for (int k=i; k<=j; ++k) f[k] = i; i = j; } for (int i=0; i<n; ++i) { int j = i; while (j+1<n&&C[j+1]==C[i]) ++j; for (int k=i; k<=j; ++k) g[k] = i; i = j; } while (q--) { int ra,ca,rb,cb; scanf("%d%d%d%d", &ra, &ca, &rb, &cb); --ra, --ca, --rb, --cb; if (f[ra]==f[rb]&&g[ca]==g[cb]&&(R[ra]+C[ca])%2==0&&(R[rb]+C[cb])%2==0) puts("YES"); else puts("NO"); } }
D
E 从后往前贪心求出每个位置可取范围, 然后从前往后贪心求字典序最小
#include <bits/stdc++.h> using namespace std; int main() { int n, l, r, k; scanf("%d%d%d%d", &n, &l, &r, &k); vector<int> a(n), L(n), R(n); for (int i=0; i<n; ++i) scanf("%d", &a[i]); L[n-1] = l, R[n-1] = r; for (int i=n-2; i>=0; --i) { L[i] = l, R[i] = r; if (a[i]>a[i+1]) { L[i] = max(L[i], L[i+1]+1); R[i] = min(R[i], R[i+1]+k); } else if (a[i]==a[i+1]) { L[i] = max(L[i], L[i+1]); R[i] = min(R[i], R[i+1]); } else { L[i] = max(L[i], L[i+1]-k); R[i] = min(R[i], R[i+1]-1); } if (L[i]>R[i]) return puts("-1"), 0; } vector<int> ans; ans.push_back(L[0]); for (int i=1; i<n; ++i) { if (a[i]>a[i-1]) { ans.push_back(max(ans.back()+1,L[i])); } else if (a[i]==a[i-1]) { ans.push_back(ans.back()); } else { ans.push_back(max(ans.back()-k,L[i])); } } for (int i=0; i<n; ++i) printf("%d%c", ans[i], " "[i+1==n]); }
F 合法点一定只能是重心, 直接暴力枚举重心, 树hash判同构即可
#include <bits/stdc++.h> using namespace std; const int P = 799898821, B = 2333333; const int N = 4e3+10; int n, sz[N], deg[N], p[N]; vector<int> g[N],h; void dfs(int x, int f) { sz[x] = 1; for (int y:g[x]) if (y!=f) { dfs(y,x); sz[x] += sz[y]; } } void dfs3(int x, int f, int tf) { vector<int> v; for (int y:g[x]) if (y!=f&&y!=tf) { dfs3(y,x,tf); v.push_back(p[y]); } sort(v.begin(),v.end()); p[x] = 1; for (auto &t:v) p[x] = ((int64_t)p[x]*B+t)%P; } void dfs2(int x, int f, int tf) { dfs3(x,-1,tf); h.push_back(p[x]); for (int y:g[x]) if (y!=f) dfs2(y,x,tf); } void gethash(int x, int y) { h.clear(); dfs2(x,y,y); sort(h.begin(),h.end()); } int main() { scanf("%d", &n); for (int i=1; i<n; ++i) { int u, v; scanf("%d%d", &u, &v); g[u].push_back(v); g[v].push_back(u); ++deg[u],++deg[v]; } int ans = -1; for (int i=1; i<=n; ++i) if (deg[i]>1) { int now_sz = -1, ok = 1; for (int j:g[i]) { dfs(j,i); if (now_sz==-1) now_sz = sz[j]; else if (now_sz!=sz[j]) { ok = 0; break; } } if (!ok) continue; vector<int> has; int cnt = 0; for (int j:g[i]) { gethash(j,i); if (has.empty()) has = h; else if (has!=h) { ok = 0; break; } ++cnt; } if (ok) ans = max(ans, cnt); } printf("%d ", ans); }
G 只用考虑每一天比$x$小的个数$cnt$, 如果存在一天使得$cnt<R$, 那么不合法, 否则合法
可以用线段树维护不合法的天数
#include <bits/stdc++.h> using namespace std; const int N = 1e5+10; int n, m, q, w, a[N], cnt[N], now[N]; vector<int> b[N]; struct { int mi,tag; void add(int x) {mi+=x;tag+=x;} } tr[N<<2]; void build(int o, int l, int r) { if (l==r) { tr[o].mi = now[l]-(int)b[l].size(); } else { int mid = (l+r)/2; build(o<<1,l,mid); build(o<<1|1,mid+1,r); tr[o].mi = min(tr[o<<1].mi, tr[o<<1|1].mi); } } void pd(int o) { if (tr[o].tag) { tr[o<<1].add(tr[o].tag); tr[o<<1|1].add(tr[o].tag); tr[o].tag = 0; } } void add(int o, int l, int r, int ql, int qr, int v) { if (ql<=l&&r<=qr) return tr[o].add(v); pd(o); int mid = (l+r)/2; if (mid>=ql) add(o<<1,l,mid,ql,qr,v); if (mid<qr) add(o<<1|1,mid+1,r,ql,qr,v); tr[o].mi = min(tr[o<<1].mi,tr[o<<1|1].mi); } int find(int o, int l, int r, int ql, int qr, int v) { if (tr[o].mi>v) return -1; int mid = (l+r)/2; if (ql<=l&&r<=qr) { if (l==r) return w=tr[o].mi, l; pd(o); if (tr[o<<1].mi<=v) return find(o<<1,l,mid,ql,qr,v); return find(o<<1|1,mid+1,r,ql,qr,v); } pd(o); if (mid>=qr) return find(o<<1,l,mid,ql,qr,v); if (mid<ql) return find(o<<1|1,mid+1,r,ql,qr,v); int t = find(o<<1,l,mid,ql,qr,v); if (t!=-1) return t; return find(o<<1|1,mid+1,r,ql,qr,v); } int main() { scanf("%d%d%d", &n, &m, &q); for (int i=1; i<=n; ++i) { scanf("%d", &a[i]); if (a[i]<a[1]) ++cnt[0]; } for (int i=1; i<=m; ++i) { int r; scanf("%d", &r); b[i].resize(r); for (int j=0; j<r; ++j) { scanf("%d", &b[i][j]); if (b[i][j]<a[1]) ++cnt[i]; } } now[1] = cnt[0]; int ans = 0; for (int i=1; i<=m; ++i) { if (now[i]<b[i].size()) ++ans, now[i+1] = cnt[i]; else now[i+1] = now[i]-b[i].size()+cnt[i]; } build(1,1,m); for (int i=1; i<=q; ++i) { int x, y, z; scanf("%d%d%d", &x, &y, &z); if (x<m) { if (z>a[1]&&b[x][y-1]<a[1]) { int pos = find(1,1,m,x+1,m,0); if (pos!=-1) { if (w==0) ++ans; add(1,1,m,x+1,pos,-1); } else add(1,1,m,x+1,m,-1); } if (z<a[1]&&b[x][y-1]>a[1]) { int pos = find(1,1,m,x+1,m,-1); if (pos!=-1) { if (w==-1) --ans; add(1,1,m,x+1,pos,1); } else add(1,1,m,x+1,m,1); } } b[x][y-1] = z; puts(ans?"0":"1"); } }
H 暴力枚举一边求另一边最大值即可, double乘法误差过大, 不要用
#include <bits/stdc++.h> using namespace std; int main() { int n; struct node { int x,y,id; bool operator < (const node &rhs) const { return x>rhs.x; } }; struct sum { void add(int id, int v) { if (a[0].second==id) a[0].first = max(a[0].first, v); else if (a[1].first<v) a[1] = {v,id}; if (a[0].first<a[1].first) swap(a[0], a[1]); } pair<int,int> a[2] = {{0,-1},{0,-1}}; } now; vector<node> v; scanf("%d", &n); int cnt = 0; uint64_t ans = 0; for (int i=0; i<n; ++i) { int x, y; scanf("%d%d", &x, &y); v.push_back({x,y,i}); v.push_back({y,x,i}); ans = max(ans, (uint64_t)x*y); } sort(v.begin(),v.end()); for (int i=0; i<v.size(); ++i) { now.add(v[i].id,v[i].y); if (now.a[1].second!=-1) ans = max(ans, (uint64_t)v[i].x*now.a[1].first*2); } if (ans%2==0) printf("%llu.0 ", ans/2); else printf("%llu.5 ", ans/2); }
I
J
'#'个数很少, 所以把中间连续很长的'.'提出来, 跑dp, 最后再统计提出来的贡献
#include <bits/stdc++.h> using namespace std; const int N = 1e5+10; int n, k, g1, g2, g3, dp[610][610]; char s[N],t[N]; void chkmax(int &a, int b) {a<b?a=b:0;} int main() { scanf("%d%d%d%d%d%s", &n, &k, &g1, &g2, &g3, s+1); int cnt = 0, m = 0; for (int i=1; i<=n; ++i) { if (s[i]=='.') { int j = i; while (j+1<n&&s[j+1]=='.') ++j; if (j-i+1>=10) { int len = j-i+1-10; if (len%2) --len; cnt += len; for (int k=0; k<j-i+1-len; ++k) t[++m] = '.'; } else { for (int k=0; k<j-i+1; ++k) t[++m] = '.'; } i = j; } else t[++m] = '#'; } memset(dp,0xef,sizeof dp); dp[0][0] = 0; for (int i=1; i<=m; ++i) { for (int j=0; j<i; ++j) { int &r = dp[i-1][j]; if (r<0) continue; chkmax(dp[i][j],r); if (t[i]=='.') chkmax(dp[i][j+1],r+g1); if (t[i]=='.'&&t[i+1]=='.') chkmax(dp[i+1][j],r+g2); if (t[i]=='.'&&t[i+1]=='#'&&t[i+2]=='.') chkmax(dp[i+2][j],r+g3); } } int ans = 0; for (int i=0; i<=k; ++i) { ans = max(ans, dp[m][i]+cnt/2*g2); int t = min(k-i,cnt); ans = max(ans, dp[m][i]+t*g1+(cnt-t)/2*g2); if (t&1) ans = max(ans, dp[m][i]+(t-1)*g1+(cnt-t+1)/2*g2); } printf("%d ", ans); }
K 线段树维护矩阵转移即可
#include <bits/stdc++.h> using namespace std; const int N = 1e5+10, P = 1e9+7; int n, q; char s[N]; struct node { int a[2][2],b[2][2],tag; node operator + (const node &rhs) const { node ret; memset(ret.a,0,sizeof ret.a); memset(ret.b,0,sizeof ret.b); ret.tag = 0; for (int k=0; k<2; ++k) for (int i=0; i<2; ++i) for (int j=0; j<2; ++j) ret.a[i][j] = (ret.a[i][j]+(int64_t)a[k][j]*rhs.a[i][k])%P, ret.b[i][j] = (ret.b[i][j]+(int64_t)b[k][j]*rhs.b[i][k])%P; return ret; } void flip() {tag^=1,swap(a,b);} } tr[N<<2]; void build(int o, int l, int r) { if (l==r) { tr[o].a[0][0] = 1; tr[o].a[0][1] = 1; tr[o].a[1][0] = 0; tr[o].a[1][1] = 1; tr[o].b[0][0] = 1; tr[o].b[0][1] = 0; tr[o].b[1][0] = 1; tr[o].b[1][1] = 1; if (s[l]=='B') swap(tr[o].a,tr[o].b); return; } int mid = (l+r)/2; build(o<<1,l,mid); build(o<<1|1,mid+1,r); tr[o] = tr[o<<1]+tr[o<<1|1]; } void pd(int o) { if (tr[o].tag) { tr[o<<1].flip(); tr[o<<1|1].flip(); tr[o].tag = 0; } } void upd(int o, int l, int r, int ql, int qr) { if (ql<=l&&r<=qr) return tr[o].flip(); pd(o); int mid = (l+r)/2; if (mid>=ql) upd(o<<1,l,mid,ql,qr); if (mid<qr) upd(o<<1|1,mid+1,r,ql,qr); tr[o] = tr[o<<1]+tr[o<<1|1]; } node query(int o, int l, int r, int ql, int qr) { if (ql<=l&&r<=qr) return tr[o]; pd(o); int mid = (l+r)/2; if (mid>=qr) return query(o<<1,l,mid,ql,qr); if (mid<ql) return query(o<<1|1,mid+1,r,ql,qr); return query(o<<1,l,mid,ql,qr)+query(o<<1|1,mid+1,r,ql,qr); } int main() { scanf("%d%d%s", &n, &q, s+1); build(1,1,n); while (q--) { int op, l, r, a, b; scanf("%d%d%d", &op, &l, &r); if (op==1) upd(1,1,n,l,r); else { node u = query(1,1,n,l,r); scanf("%d%d", &a, &b); int ansa = ((int64_t)u.a[0][0]*a+(int64_t)u.a[0][1]*b)%P; int ansb = ((int64_t)u.a[1][0]*a+(int64_t)u.a[1][1]*b)%P; printf("%d %d ", ansa, ansb); } } }
2019 ccpc final
A 模拟
B
连一条$(1,1)$的自环, 保证最终路径最终一定无限循环
从起点开始贪心, 维护一个可取集合, 每次走最小, 这样可以$O(nm)$求出一条长$3n$的最小路径
那么前$n$步中某一步会进入一个环, 然后$kmp$在[n+1,3n]中找环即可
#include <bits/stdc++.h> using namespace std; const int N = 2e3+10, P = 1e9+7; int n, m, vis[N], fail[N*2], pw[N*2]; vector<pair<int,int>> g[N],f[N]; int qpow(int a, int n) { int ans = 1; for (; n; n>>=1,a=(int64_t)a*a%P) if (n&1) ans=(int64_t)ans*a%P; return ans; } void dfs(int x) { if (vis[x]) return; vis[x] = 1; for (auto &e:f[x]) dfs(e.first); } vector<int> get_cyc(vector<int> v) { fail[0] = fail[1] = 0; int j = 0; for (int i=1; i<v.size(); ++i) { while (j&&v[i]!=v[j]) j=fail[j]; if (v[i]==v[j]) ++j; fail[i+1] = j; } int len = v.size()-fail[v.size()]; v.resize(len); return v; } int calc(vector<int> v) { int num = 0; for (int t:v) num = (num*10ll+t)%P; return num; } int main() { pw[0] = 1; for (int i=1; i<2*N; ++i) pw[i] = pw[i-1]*700000005ll%P; int t; scanf("%d", &t); for (int clk=1; clk<=t; ++clk) { scanf("%d%d", &n, &m); for (int i=0; i<n; ++i) { g[i].clear(), f[i].clear(); vis[i] = 0; } for (int i=0; i<m; ++i) { int u, v, w; scanf("%d%d%d", &u, &v, &w); g[u].push_back({v,w}); f[v].push_back({u,w}); } g[1].push_back({1,0}); dfs(1); vector<int> ans, cur; cur.push_back(0); for (int i=0; i<3*n; ++i) { int mi = 1e9; for (int x:cur) for (auto e:g[x]) if (vis[e.first]) mi = min(mi, e.second); vector<int> nxt(n); for (int x:cur) for (auto e:g[x]) if (vis[e.first]&&e.second==mi) nxt[e.first] = 1; ans.push_back(mi); cur.clear(); for (int i=0; i<n; ++i) if (nxt[i]) cur.push_back(i); } vector<int> ans2(ans.begin()+n,ans.end()); ans.resize(n); ans2 = get_cyc(ans2); int x = calc(ans), y = calc(ans2); y = (int64_t)y*pw[ans.size()+ans2.size()]%P*qpow(1-pw[ans2.size()],P-2)%P; x = (int64_t)x*pw[ans.size()]%P; x = (x+y)%P; if (x<0) x += P; printf("Case #%d: %d ", clk, x); } }
C
D
E
选了一个点后, 要删掉周围一个菱形区域, 对坐标分块, 暴力检验附近的点即可
F
G
H
中间的SAD直接算在答案里, 然后每个串左侧只有是$D,AD$才有贡献,右侧只有是$S,SA$才有贡献,或者是单独一个$A$
大力贪心分类讨论即可
I
简单构造, 把(i,j)放在(j,1,i),(j,2,i)即可
J
K 直接dsu on tree可过
L 暴力找规律, 特判$1 imes 1,1 imes 2$, 否则答案为$2(n+m-2)$
The 13th Chinese Northeast Collegiate Programming Contest
A
B
枚举分母, 堆贪心求分子最大值
#include <bits/stdc++.h> using namespace std; int64_t gcd(int64_t a, int64_t b) {return b?gcd(b,a%b):a;} int main() { int t; scanf("%d", &t); while (t--) { int n, m; scanf("%d%d", &n, &m); vector<int> l(m); vector<vector<int>> g(m); for (int i=0; i<m; ++i) scanf("%d", &l[i]); for (int i=0; i<n; ++i) { int a, b; scanf("%d%d", &a, &b); --b; g[b].push_back(a); } for (int i=0; i<m; ++i) sort(g[i].begin(),g[i].end(),greater<int>()); vector<int> id(m); for (int i=0; i<m; ++i) id[i] = i; sort(id.begin(),id.end(),[&](int a,int b){return l[a]<l[b];}); int now = 0; struct Q { int id,pos; bool operator < (const Q &rhs) const { return pos>rhs.pos; } }; priority_queue<Q> q; int64_t sum = 0; int64_t ansx = 0, ansy = 0; for (int i=1; i<=n; ++i) { while (now<m&&l[id[now]]<=i) q.push({id[now++],0}); while (q.size()&&q.top().pos<i) { auto u = q.top(); q.pop(); if (u.pos<g[u.id].size()) { sum += g[u.id][u.pos++]; q.push(u); } } if (i==1) ansx = sum, ansy = 1; else if (ansx*i<sum*ansy) ansx = sum, ansy = i; } int64_t t = gcd(ansx, ansy); printf("%lld/%lld ", ansx/t, ansy/t); } }
C
简单几何
#include <bits/stdc++.h> using namespace std; int n; int64_t ans; int gcd(int a, int b) {return b?gcd(b,a%b):a;} pair<int,int> reduce(int x, int y) { int g = gcd(abs(x),abs(y)); x /= g, y /= g; if (x<0) x = -x, y = -y; return {x,y}; } int main() { int t; scanf("%d", &t); while (t--) { int n; scanf("%d", &n); ans = n*(n-1ll)/2; struct node { map<int64_t,int> m; int sz; void add(int64_t x) { if (m.empty()) sz = m[x] = 1; else ::ans -= sz++-m[x]++; } } s; map<pair<int,int>,node> mp; for (int i=0; i<n; ++i) { int x1,x2,y1,y2; scanf("%d%d%d%d",&x1,&y1,&x2,&y2); int x, y; tie(x,y) = reduce(x1-x2,y1-y2); if (!x) s.add(x1); else mp[{x,y}].add((int64_t)y*(-x2)+(int64_t)y2*x); } printf("%lld ", ans); } }
D
询问离线, 建虚树暴力即可
#include <bits/stdc++.h> using namespace std; const int N = 5e5+10; int clk, dep[N], L[N], fa[N], val[N]; vector<int> g[N]; struct edge {int to,len,w;} f[N]; bool cmp(int a, int b) {return L[a]<L[b];} void dfs(int x, int f, int d) { dep[x] = d, fa[x] = f, L[x] = ++clk; for (int y:g[x]) if (y!=f) dfs(y,x,d+1); } int lca(int x, int y) { while (x!=y) { if (dep[x]<dep[y]) y = fa[y]; else x = fa[x]; } return x; } void build(vector<int> a) { sort(a.begin(),a.end(),cmp); a.erase(unique(a.begin(),a.end()),a.end()); vector<int> s; s.push_back(a[0]); for (int i=1; i<a.size(); ++i) { int l = lca(a[i], a[i-1]); if (dep[l]!=dep[s.back()]) { while (s.size()>1&&dep[s[s.size()-2]]>=dep[l]) { f[s.back()] = {s[s.size()-2],dep[s.back()]-dep[s[s.size()-2]]-1,0}; s.pop_back(); } if (dep[s.back()]>dep[l]) { f[s.back()] = {l,dep[s.back()]-dep[l]-1,0}; s.back() = l; } } s.push_back(a[i]); } while (s.size()>1) { f[s.back()] = {s[s.size()-2],dep[s.back()]-dep[s[s.size()-2]]-1,0}; s.pop_back(); } } int main() { int t; scanf("%d", &t); while (t--) { int n, m; scanf("%d%d", &n, &m); struct Q {int op, u, v, k;}; vector<Q> q(m); clk = 0; for (int i=1; i<=n; ++i) { g[i].clear(); val[i] = 0; } for (int i=0; i<n-1; ++i) { int u, v; scanf("%d%d", &u, &v); g[u].push_back(v); g[v].push_back(u); } dfs(1,0,0); vector<int> v; for (int i=0; i<m; ++i) { scanf("%d%d%d", &q[i].op, &q[i].u, &q[i].v); if (q[i].op<=3||q[i].op==7) scanf("%d", &q[i].k); v.push_back(q[i].u); v.push_back(q[i].v); } build(v); for (int i=0; i<m; ++i) { int x = q[i].u, y = q[i].v, k = q[i].k; if (q[i].op==1) { while (x!=y) { if (dep[x]<dep[y]) swap(x, y); val[x] += k; if (f[x].len) f[x].w += k; x = f[x].to; } val[x] += k; } else if (q[i].op==2) { while (x!=y) { if (dep[x]<dep[y]) swap(x, y); val[x] ^= k; if (f[x].len) f[x].w ^= k; x = f[x].to; } val[x] ^= k; } else if (q[i].op==3) { while (x!=y) { if (dep[x]<dep[y]) swap(x, y); if (val[x]>=k) val[x] -= k; if (f[x].len&&f[x].w>=k) f[x].w -= k; x = f[x].to; } if (val[x]>=k) val[x] -= k; } else if (q[i].op==4) { int64_t ans = 0; while (x!=y) { if (dep[x]<dep[y]) swap(x, y); ans += val[x]; if (f[x].len) ans += (int64_t)f[x].w*f[x].len; x = f[x].to; } ans += val[x]; printf("%lld ", ans); } else if (q[i].op==5) { int ans = 0; while (x!=y) { if (dep[x]<dep[y]) swap(x, y); ans ^= val[x]; if (f[x].len&1) ans ^= f[x].w; x = f[x].to; } ans ^= val[x]; printf("%d ", ans); } else if (q[i].op==6) { int ma = -2e9, mi = 2e9; while (x!=y) { if (dep[x]<dep[y]) swap(x, y); ma = max(ma, val[x]); mi = min(mi, val[x]); if (f[x].len) { ma = max(ma, f[x].w); mi = min(mi, f[x].w); } x = f[x].to; } ma = max(ma, val[x]), mi = min(mi, val[x]); printf("%d ", ma-mi); } else { int mi = 2e9; while (x!=y) { if (dep[x]<dep[y]) swap(x, y); mi = min(mi, abs(k-val[x])); if (f[x].len) mi = min(mi, abs(k-f[x].w)); x = f[x].to; } mi = min(mi, abs(k-val[x])); printf("%d ", mi); } } } }
E
考虑每个点的邻接边, 用最小边与其他边连通即可
#include <bits/stdc++.h> using namespace std; int main() { int t; scanf("%d", &t); while (t--) { int n; scanf("%d", &n); vector<vector<int>> g(n); for (int i=1; i<n; ++i) { int u, v, w; scanf("%d%d%d", &u, &v, &w); --u, --v; g[u].push_back(w); g[v].push_back(w); } int64_t ans = 0; for (int i=0; i<n; ++i) if (g[i].size()>1) { int mi = 2e9; for (auto &t:g[i]) ans += t, mi = min(mi, t); ans += (g[i].size()-2ll)*mi; } printf("%lld ", ans); } }
F
G
x轴y轴可以独立考虑, 转化为给定一些区间, 求一个位置使得所有区间到它距离和最小, 坐标离散化后枚举即可
也可以直接用中位数做
#include <bits/stdc++.h> using namespace std; int main() { int t; scanf("%d", &t); while (t--) { int n; scanf("%d", &n); vector<pair<int,int>> col(n), raw(n); for (int i=0; i<n; ++i) scanf("%d%d%d%d", &col[i].first, &raw[i].first, &col[i].second, &raw[i].second); auto solve = [&](vector<pair<int,int>> g) { vector<pair<int,int>> events; int64_t suml = 0, sumr = 0; for (int i=0; i<g.size(); ++i) { sumr += g[i].first; events.push_back({g[i].first,1}); events.push_back({g[i].second+1,-1}); } sort(events.begin(),events.end()); int cntl = 0, cntr = g.size()-1; sumr -= events[0].first; int64_t ans = 1e18; for (int i=1; i<events.size(); ++i) { int l = events[i-1].first, r = events[i].first-1; if (l<=r) { if (cntl>cntr) { ans = min(ans, (int64_t)cntl*l-suml+sumr-(int64_t)cntr*l); } else { ans = min(ans, (int64_t)cntl*r-suml+sumr-(int64_t)cntr*r); } } if (events[i].second<0) suml += events[i].first-1, ++cntl; else sumr -= events[i].first, --cntr; } return ans; }; printf("%lld ", solve(col)+solve(raw)); } }
H
一个区间答案是总和减去相邻数最小值的和, 直接用线段树维护
#include <bits/stdc++.h> using namespace std; const int N = 1e5+10; int a[N]; int64_t tr[N<<2],tag[N<<2]; int64_t tr2[N<<2],tag2[N<<2]; void build(int o, int l, int r) { tag[o] = 0; if (l==r) tr[o] = a[l]; else { int mid = (l+r)/2; build(o<<1,l,mid); build(o<<1|1,mid+1,r); tr[o] = tr[o<<1]+tr[o<<1|1]; } } void build2(int o, int l, int r) { tag2[o] = 0; if (l==r) tr2[o] = min(a[l], a[l+1]); else { int mid = (l+r)/2; build2(o<<1,l,mid); build2(o<<1|1,mid+1,r); tr2[o] = tr2[o<<1]+tr2[o<<1|1]; } } void pd(int64_t tr[], int64_t tag[], int o, int l, int r) { if (tag[o]) { tag[o<<1] += tag[o]; tag[o<<1|1] += tag[o]; int mid = (l+r)/2; tr[o<<1] += tag[o]*(mid-l+1); tr[o<<1|1] += tag[o]*(r-mid); tag[o] = 0; } } void add(int64_t tr[], int64_t tag[], int o, int l, int r, int ql, int qr, int64_t v) { if (ql<=l&&r<=qr) { tr[o] += (int64_t)(r-l+1)*v; tag[o] += v; return; } pd(tr,tag,o,l,r); int mid = (l+r)/2; if (mid>=ql) add(tr,tag,o<<1,l,mid,ql,qr,v); if (mid<qr) add(tr,tag,o<<1|1,mid+1,r,ql,qr,v); tr[o] = tr[o<<1]+tr[o<<1|1]; } int64_t query(int64_t tr[], int64_t tag[], int o, int l, int r, int ql, int qr) { if (ql<=l&&r<=qr) return tr[o]; pd(tr,tag,o,l,r); int mid = (l+r)/2; int64_t ans = 0; if (mid>=ql) ans += query(tr,tag,o<<1,l,mid,ql,qr); if (mid<qr) ans += query(tr,tag,o<<1|1,mid+1,r,ql,qr); return ans; } void upd(int o, int l, int r, int x, int64_t v) { if (l==r) tr2[o] = v; else { pd(tr2,tag2,o,l,r); int mid = (l+r)/2; if (mid>=x) upd(o<<1,l,mid,x,v); else upd(o<<1|1,mid+1,r,x,v); tr2[o] = tr2[o<<1]+tr2[o<<1|1]; } } int main() { int t; scanf("%d", &t); while (t--) { int n, m; scanf("%d%d", &n, &m); for (int i=0; i<n; ++i) scanf("%d", &a[i]); build(1,0,n-1); if (n!=1) build2(1,0,n-2); while (m--) { int op, l, r, k; scanf("%d%d%d", &op, &l, &r); --l, --r; if (op==1) { scanf("%d", &k); add(tr,tag,1,0,n-1,l,r,k); if (l!=r) add(tr2,tag2,1,0,n-2,l,r-1,k); if (l) { int64_t w = min(query(tr,tag,1,0,n-1,l-1,l-1),query(tr,tag,1,0,n-1,l,l)); upd(1,0,n-2,l-1,w); } if (r!=n-1) { int64_t w = min(query(tr,tag,1,0,n-1,r,r),query(tr,tag,1,0,n-1,r+1,r+1)); upd(1,0,n-2,r,w); } } else { int64_t ans = query(tr,tag,1,0,n-1,l,r); if (l!=r) ans -= query(tr2,tag2,1,0,n-2,l,r-1); printf("%lld ", ans); } } } }
I
J
签到
2018 icpc Seoul
A
区间离散化, 枚举右侧直线, 线段树维护左侧直线贡献
#include <bits/stdc++.h> using namespace std; const int N = 1e6+10; int mx[N<<2],tag[N<<2]; void add(int o, int l, int r, int ql, int qr, int v) { if (ql<=l&&r<=qr) mx[o]+=v,tag[o]+=v; else { if (tag[o]) { tag[o<<1] += tag[o]; tag[o<<1|1] += tag[o]; mx[o<<1] += tag[o]; mx[o<<1|1] += tag[o]; tag[o] = 0; } int mid = (l+r)/2; if (mid>=ql) add(o<<1,l,mid,ql,qr,v); if (mid<qr) add(o<<1|1,mid+1,r,ql,qr,v); mx[o] = max(mx[o<<1],mx[o<<1|1]); } } int main() { int n; scanf("%d", &n); vector<pair<int,int>> interval,events; vector<int> b; interval.push_back({-1,-1}); for (int i=1; i<=n; ++i) { int ux, uy, vx, vy; scanf("%d%d%d%d", &ux, &uy, &vx, &vy); interval.push_back({vy,uy}); events.push_back({vy,i}); events.push_back({uy+1,-i}); b.push_back(vy); b.push_back(uy); } sort(b.begin(),b.end()); b.erase(unique(b.begin(),b.end()),b.end()); for (auto &t:interval) { t.first = lower_bound(b.begin(),b.end(),t.first)-b.begin(); t.second = lower_bound(b.begin(),b.end(),t.second)-b.begin(); } sort(events.begin(),events.end()); set<int> cur; int ans = 0; cur.insert(events[0].second); for (int i=1; i<events.size(); ++i) { int len = events[i].first-events[i-1].first; if (len) { ans = max(ans, (int)cur.size()+mx[1]); } int id = events[i].second; if (id>0) { cur.insert(id); } else { id = -id; cur.erase(id); add(1,0,b.size()-1,interval[id].first,interval[id].second,1); } } printf("%d ", ans); }
B
转化为给定有向图, 求任意点对路径最小值最大值, 可以直接floyd
#include <bits/stdc++.h> using namespace std; const int N = 555; int n, m, c[N][N], deg[N], g[N][N]; int main() { scanf("%d%d", &n, &m); for (int i=0; i<m; ++i) { vector<int> v(n); for (int j=0; j<n; ++j) scanf("%d", &v[j]); for (int j=0; j<n; ++j) if (v[j]) { for (int k=0; k<n; ++k) if (k!=j) { if (v[k]>v[j]||!v[k]) ++c[j][k]; } } } for (int i=0; i<n; ++i) { for (int j=0; j<n; ++j) { if (c[i][j]>c[j][i]) g[i][j] = c[i][j]; } } for (int k=0; k<n; ++k) { for (int i=0; i<n; ++i) { for (int j=0; j<n; ++j) { g[i][j] = max(g[i][j], min(g[i][k],g[k][j])); } } } vector<int> v; for (int i=0; i<n; ++i) { int ok = 1; for (int j=0; j<n; ++j) if (g[i][j]<g[j][i]) ok = 0; if (ok) v.push_back(i+1); } for (int i=0; i<v.size(); ++i) printf("%d%c", v[i], " "[i+1==v.size()]); }
C
D 签到
E
考虑二分答案, 先考虑左右两段的位置, 最后再考虑中间段
V1显然取越大越好, 但V2不一定是越小越好, 因为可能导致中间段L1的值超过L2
可以直接维护每个位置$L2$最大取值, 然后枚举中间段判断即可
#include <bits/stdc++.h> using namespace std; const int N = 3e5+10; int n; pair<int,int> a[N]; int chk(int x) { int pos = -1; for (int i=1; i<=n; ++i) { if (a[i].second>x) { pos = i; break; } } if (pos==-1) return 1; int ma = a[n].second, mi = a[n].second, posr = -1; vector<int64_t> p(n+1); for (int i=n; i; --i) { ma = max(ma, a[i].second); mi = min(mi, a[i].second); int64_t lx = max(0, ma-x), rx = (int64_t)mi+x; if (lx<=rx) p[i] = rx; else { posr = i; break; } } if (posr==-1) return 1; if (pos>posr) return 1; ma = a[pos].second, mi = a[pos].second; for (int i=pos; i<=n; ++i) { ma = max(ma, a[i].second); mi = min(mi, a[i].second); int64_t lx = max(0, ma-x), rx = (int64_t)mi+x; if (lx<=rx) { if (i==n) return 1; if (i>=posr&&lx<=p[i+1]) return 1; } } return 0; } int main() { scanf("%d", &n); int l = 0, r = 2e9+10, ans = -1; for (int i=1; i<=n; ++i) { scanf("%d%d", &a[i].first, &a[i].second), a[i].second *= 2; if (a[i].first==0) { l = max(l, a[i].second); } } sort(a+1,a+1+n); while (l<=r) { int mid = ((int64_t)l+r)/2; if (chk(mid)) ans = mid, r = mid-1; else l = mid+1; } assert(ans!=-1); printf("%.1lf ", ans/2.); }
F
模拟
#include <bits/stdc++.h> using namespace std; const int N = 1e5+10; int nxt[N],ok=1; string s; int isop(char x) { return x=='+'||x=='-'||x=='*'||x=='/'||x=='%'; } pair<int,int> dfs(int l, int r) { if (l>r) return {0,0}; if (s[l]=='(') { int t = dfs(l+1,nxt[l]-1).first; if (t==0) { puts("error"); exit(0); } if (t==1) ok = 0; if (t>2) ok = 0; int pos = nxt[l]+1; if (pos>r) return {1,1}; if (!isop(s[pos])) { puts("error"); exit(0); } t = dfs(pos+1,r).first; if (t==0) { puts("error"); exit(0); } int ans = t+1; if (ans>=3) ok = 0; return {ans,0}; } if (!isalpha(s[l])) { puts("error"); exit(0); } int pos = l+1; if (pos>r) return {1,0}; if (!isop(s[pos])) { puts("error"); exit(0); } int t = dfs(pos+1,r).first; if (t==0) { puts("error"); exit(0); } int ans = t+1; if (ans>=3) ok = 0; return {ans,0}; } int main() { getline(cin,s); string tmp; for (auto &c:s) if (c!=' ') tmp.push_back(c); s = tmp; vector<int> sta; for (int i=0; i<s.size(); ++i) { if (s[i]=='(') sta.push_back(i); else if (s[i]==')') { if (sta.empty()) return puts("error"),0; nxt[sta.back()] = i; sta.pop_back(); } } if (sta.size()) return puts("error"),0; if (dfs(0,s.size()-1).second) ok = 0; puts(ok?"proper":"improper"); }
G
H
I
J
状态(u,v)表示从一个人类点走到u,从一个非人类点走到v, 暴力bfs即可
#include <bits/stdc++.h> using namespace std; const int N = 1e3+10; int n, w, c, h, m; int a[N], b[N], vis[N][N]; vector<pair<int,int>> g[N]; int main() { scanf("%d%d%d%d%d", &n, &w, &c, &h, &m); for (int i=0; i<h; ++i) { int x; scanf("%d", &x); a[x] = 1; } for (int i=0; i<m; ++i) { int x; scanf("%d", &x); b[x] = 1; } for (int i=0; i<w; ++i) { int u, v, w; scanf("%d%d%d", &u, &w, &v); g[u].push_back({v,w}); } queue<pair<int,int>> q; for (int i=0; i<n; ++i) if (a[i]) { for (int j=0; j<n; ++j) if (!a[j]) { q.push({i,j}); vis[i][j] = 1; } } while (q.size()) { auto u = q.front(); q.pop(); if (b[u.first]&&b[u.second]) return puts("YES"),0; for (auto x:g[u.first]) for (auto y:g[u.second]) { if (x.second==y.second) { if (!vis[x.first][y.first]) { q.push({x.first,y.first}); vis[x.first][y.first] = 1; } } } } puts("NO"); }
K
2sat板子
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 1e6+10; struct TwoSAT { int n; vector<int> g[N<<1]; bool mark[N<<1]; int s[N<<1], c; bool dfs(int x) { if (mark[x^1]) return 0; if (mark[x]) return 1; mark[x] = 1; s[c++] = x; for (int y:g[x]) if (!dfs(y)) return 0; return 1; } //x==xv or y==yv void add(int x, int xv, int y, int yv) { x = x*2+xv; y = y*2+yv; g[x^1].push_back(y); g[y^1].push_back(x); } bool solve() { for (int i=0; i<n*2; i+=2) { if (!mark[i]&&!mark[i+1]) { c = 0; if (!dfs(i)) { while (c) mark[s[--c]]=0; if (!dfs(i+1)) return 0; } } } return 1; } } solver; int main() { int n, k; scanf("%d%d", &n, &k); solver.n = n; for (int i=1; i<=k; ++i) { int a, b, c; char x, y, z; scanf(" %d %c %d %c %d %c", &a, &x, &b, &y, &c, &z); solver.add(a,x=='B',b,y=='B'); solver.add(a,x=='B',c,z=='B'); solver.add(b,y=='B',c,z=='B'); } if (!solver.solve()) return puts("-1"), 0; for (int i=1; i<=n; ++i) printf("%c", solver.mark[i<<1]?'R':'B'); puts(""); }
L 签到
2012-2013 ACM-ICPC Pacific Northwest Regional Contest
A 签到
B 签到
C
D 直接用pair排序即可
#include <bits/stdc++.h> using namespace std; int main() { int n, w, h; while (scanf("%d%d%d", &n, &w, &h),n) { vector<pair<int,int>> a(n); for (int i=0; i<n; ++i) scanf("%d%d", &a[i].first, &a[i].second); sort(a.begin(),a.end()); for (int i=0; i<n/2; ++i) printf("%d %d ", a[i].first, a[i].second); } }
E 3sat问题, 直接暴力即可
#include <bits/stdc++.h> using namespace std; int rings, runes; int64_t A[111],B[111],C[111]; int main() { int t; scanf("%d", &t); while (t--) { scanf("%d%d", &rings, &runes); int err = 0; for (int i=0; i<runes; ++i) { int64_t a, b, c; scanf("%lld%lld%lld%*d", &a, &b, &c); if (!a||!b||!c) err = 1; if (err==1) continue; if (abs(a)>rings||abs(b)>rings||abs(c)>rings) err = 2; if (err==2) continue; if (abs(a)==abs(b)||abs(a)==abs(c)||abs(b)==abs(c)) err = 3; if (err==3) continue; A[i] = a, B[i] = b, C[i] = c; } if (err) { if (err==1) puts("INVALID: NULL RING"); if (err==2) puts("INVALID: RING MISSING"); if (err==3) puts("INVALID: RUNE CONTAINS A REPEATED RING"); continue; } int ok = 0; for (int i=0; i<(1<<rings); ++i) { for (int j=0; j<=runes; ++j) { if (j==runes) ok = 1; if ((A[j]>0)==((i>>abs(A[j])-1)&1)) continue; if ((B[j]>0)==((i>>abs(B[j])-1)&1)) continue; if ((C[j]>0)==((i>>abs(C[j])-1)&1)) continue; break; } if (ok) break; } if (ok) puts("RUNES SATISFIED!"); else puts("RUNES UNSATISFIABLE! TRY ANOTHER GATE!"); } }
F
G 简单数位dp
#include <bits/stdc++.h> using namespace std; int64_t dp[100][3][2]; int main() { int64_t n; while (~scanf("%lld", &n)) { memset(dp,0,sizeof dp); dp[61][0][1] = 1; int64_t ans = -1; for (int i=60; i>=0; --i) for (int d=0; d<3; ++d) { for (int lim=0; lim<=1; ++lim) { int64_t &res = dp[i+1][d][lim]; if (res==0) continue; int mx = lim?n>>i&1:1; for (int z=0; z<=mx; ++z) { if (i==0) { if ((d+z)%3==0) ans += res; } else dp[i][(d+z)%3][lim&&z==mx] += res; } } } printf("Day %lld: Level = %lld ", n, ans); } }
H 树状数组求逆序对
#include <bits/stdc++.h> using namespace std; int c[100010], a[100010]; map<string,int> f; int main() { int n; ios::sync_with_stdio(0); cin.tie(0); while (cin>>n, n) { f.clear(); for (int i=1; i<=n; ++i) { string s; cin>>s; f[s] = i; } for (int i=1; i<=n; ++i) { string s; cin>>s; a[i] = f[s]; } int64_t ans = 0; for (int i=1; i<=n; ++i) c[i] = 0; for (int i=1; i<=n; ++i) { for (int j=a[i]; j<=n; j+=j&-j) ans += c[j]; for (int j=a[i]; j; j^=j&-j) ++c[j]; } printf("%lld ", ans); } }
I
J
K 每个字符'I', 建个点限流, 跑最大流
#include <bits/stdc++.h> using namespace std; const int N = 1e6+10, S = N-2, T = N-1, INF = 0x3f3f3f3f; int n, m; struct edge { int to,w,next; edge(int to=0,int w=0,int next=0):to(to),w(w),next(next){} } e[N]; int head[N], dep[N], vis[N], cur[N], cnt; queue<int> Q; int bfs() { for (int i=1; i<=2*n*m; ++i) dep[i]=INF,vis[i]=0,cur[i]=head[i]; dep[S]=INF,vis[S]=0,cur[S]=head[S]; dep[T]=INF,vis[T]=0,cur[T]=head[T]; dep[S]=0,Q.push(S); while (Q.size()) { int u = Q.front(); Q.pop(); for (int i=head[u]; i; i=e[i].next) { if (dep[e[i].to]>dep[u]+1&&e[i].w) { dep[e[i].to]=dep[u]+1; Q.push(e[i].to); } } } return dep[T]!=INF; } int dfs(int x, int w) { if (x==T) return w; int used = 0; for (int i=cur[x]; i; i=e[i].next) { cur[x] = i; if (dep[e[i].to]==dep[x]+1&&e[i].w) { int f = dfs(e[i].to,min(w-used,e[i].w)); if (f) used+=f,e[i].w-=f,e[i^1].w+=f; if (used==w) break; } } return used; } int dinic() { int ans = 0; while (bfs()) ans+=dfs(S,INF); return ans; } void add(int u, int v, int w) { e[++cnt] = edge(v,w,head[u]); head[u] = cnt; e[++cnt] = edge(u,0,head[v]); head[v] = cnt; } string s[50]; const int dx[]={0,0,-1,1}; const int dy[]={-1,1,0,0}; int main() { while (1) { string tmp; n = 0; while (1) { if (!getline(cin,tmp)) break; if (tmp.size()) s[n++] = tmp; else break; } m = s[0].size(); if (!n) break; cnt = 1; for (int i=1; i<=2*n*m; ++i) head[i] = 0; head[S] = head[T] = 0; for (int i=0; i<n; ++i) { for (int j=0; j<m; ++j) { int id = i*m+j+1; if (s[i][j]=='W') add(S,id,1); else if (s[i][j]=='N') add(id,T,1); else if (s[i][j]=='I') { add(id,id+n*m,1); for (int k=0; k<4; ++k) { int x=i+dx[k], y=j+dy[k]; if (x<0||y<0||x>=n||y>=m) continue; if (s[x][y]=='W') add(x*m+y+1,id,1); if (s[x][y]=='N') add(id+n*m,x*m+y+1,1); } } } } printf("%d ", dinic()); } }
L 模拟, 读入很坑, 数据有空行
#include <bits/stdc++.h> using namespace std; char get(char x) { if (x=='a') return 'e'; if (x=='i') return 'o'; if (x=='y') return 'u'; if (x=='e') return 'a'; if (x=='o') return 'i'; if (x=='u') return 'y'; string t = "bkxznhdcwgpvjqtsrlmf"; int m = t.size(); for (int i=0; i<m; ++i) if (x==t[i]) { return t[(i+10)%m]; } throw; } int main() { while (1) { char c; string s; do { c=getchar(); if (32<=c&&c<=126) s.push_back(c); } while (c!=EOF&&c!=' '); if (c==EOF) break; for (int i=0; i<s.size(); ++i) { if (isupper(s[i])) s[i] = get(s[i]-'A'+'a')-'a'+'A'; else if (islower(s[i])) s[i] = get(s[i]); } cout<<s<<' '; } }
2017-2018 ACM-ICPC Latin American Regional Programming Contest
A
B 跑暴力找规律, 发现答案只跟中间非元音个数有关
#include <bits/stdc++.h> using namespace std; const int N = 1e5+10; char s[N]; int chk(char x) { return x=='a'||x=='e'||x=='i'||x=='o'||x=='u'; } int main() { scanf("%s", s+1); int cnt = 0, n = strlen(s+1); for (int i=1; i<=n; ++i) cnt += chk(s[i]); if (!cnt) return puts("1"), 0; if (!chk(s[1])) return puts("0"),0; cnt = (cnt+1)/2; int now = 0; for (int i=1; i<=n; ++i) { if (chk(s[i])&&++now==cnt) { int j = i; while (j+1<=n&&!chk(s[j+1])) ++j; printf("%d ", j-i+1); return 0; } } }
C k很小, 直接暴力
#include <bits/stdc++.h> using namespace std; int n, k, a[1010]; map<int,int> s; void add(int x) { ++s[x]; } void del(int x) { if (!--s[x]) s.erase(x); } int main() { scanf("%d%d", &k, &n); for (int i=0; i<n; ++i) { int t; scanf("%d", &t); ++a[t]; } for (int i=1; i<=k; ++i) add(a[i]); for (int i=1; i<=k; ++i) { del(a[i]); ++a[i]; add(a[i]); if (s.size()==1) printf("+%d ", i),exit(0); del(a[i]); a[i] -= 2; add(a[i]); if (s.size()==1) printf("-%d ", i),exit(0); del(a[i]); ++a[i]; add(a[i]); } for (int i=1; i<=k; ++i) { for (int j=1; j<=k; ++j) { del(a[i]),del(a[j]); --a[i],++a[j]; add(a[i]),add(a[j]); if (s.size()==1) printf("-%d +%d ", i, j),exit(0); del(a[i]),del(a[j]); ++a[i],--a[j]; add(a[i]),add(a[j]); } } puts("*"); }
D 数据随机, 同色区间非常少, 直接珂朵莉树
#include <bits/stdc++.h> using namespace std; const int N = 1e5+10; int l, c, n, vis[N]; struct v:map<int,pair<int,int>> { auto split(int x) { auto p = lower_bound(x); if (p!=end()&&p->first==x) return p; --p; if (x>p->second.first) return end(); auto t = *p; erase(p); emplace(t.first,pair<int,int>(x-1,t.second.second)); return emplace(x,t.second).first; } } mp; int main() { scanf("%d%d%d", &l, &c, &n); mp[0] = {l-1, 1}; for (int i=1; i<=n; ++i) { int p, x, a, b, s = 0; scanf("%d%d%d%d", &p, &x, &a, &b); for (auto &t:mp) if (t.second.second==p) s += t.second.first-t.first+1; int m1 = (a+(int64_t)s*s)%l; int m2 = (a+(int64_t)(s+b)*(s+b))%l; if (m1>m2) swap(m1, m2); auto R = mp.split(m2+1), L = mp.split(m1); mp.erase(L,R); mp[m1] = {m2,x}; } int ans = 0; for (auto &t:mp) ans = max(ans, vis[t.second.second]+=t.second.first-t.first+1); printf("%d ", ans); }
E dp求出后缀模k是否能为x, 然后从前往后贪心
#include <bits/stdc++.h> using namespace std; const int N = 1111; char s[N]; int k,pw[N],dp[N][N]; int main() { scanf("%s%d", s+1, &k); int n = strlen(s+1); pw[0] = 1; for (int i=1; i<=n; ++i) pw[i] = pw[i-1]*10%k; dp[n+1][0] = 1; for (int i=n; i; --i) { for (int j=0; j<k; ++j) if (dp[i+1][j]) { int l = i==1, r = 9; if (s[i]!='?') l = s[i]-'0', r = s[i]-'0'; for (int x=l; x<=r; ++x) dp[i][(j+x*pw[n-i])%k] = 1; } } if (!dp[1][0]) return puts("*"), 0; int now = 0; for (int i=1; i<=n; ++i) { int l = i==1, r = 9; if (s[i]!='?') l = s[i]-'0', r = s[i]-'0'; for (int x=l; x<=r; ++x) { if (dp[i+1][((now-x*pw[n-i])%k+k)%k]) { now = ((now-x*pw[n-i])%k+k)%k; s[i] = x+'0'; break; } } } puts(s+1); }
F 一维排序, 另一维树状数组优化
#include <bits/stdc++.h> using namespace std; const int N = 1e5+10; int n, b[N]; struct node { int x,y; int64_t w; bool operator < (const node &rhs) const { if (x!=rhs.x) return x<rhs.x; return y<rhs.y; } } a[N]; int64_t c[N],dp[N]; int main() { scanf("%d", &n); int cnt = 0; for (int i=1; i<=n; ++i) { scanf("%d%d%lld", &a[i].x, &a[i].y, &a[i].w); b[++cnt] = a[i].y; } sort(b+1, b+1+cnt); cnt = unique(b+1, b+1+cnt)-b-1; for (int i=1; i<=n; ++i) { a[i].y = lower_bound(b+1,b+1+cnt,a[i].y)-b; } sort(a+1, a+1+n); int m = 1; for (int i=2; i<=n; ++i) { if (a[i].x==a[m].x&&a[i].y==a[m].y) a[m].w += a[i].w; else a[++m] = a[i]; } n = m; int64_t ans = 0; for (int i=1; i<=n; ++i) { int j = i; while (j+1<=n&&a[j+1].x==a[i].x) ++j; for (int k=i; k<=j; ++k) { dp[k] = a[k].w; for (int x=a[k].y-1; x; x^=x&-x) dp[k] = max(dp[k], c[x]+a[k].w); } for (int k=i; k<=j; ++k) { for (int x=a[k].y; x<=cnt; x+=x&-x) c[x] = max(c[x], dp[k]); } i = j; } printf("%lld ", *max_element(dp+1,dp+1+n)); }
G dp[x][0/1][0/1][0/1]表示不考虑故障点返回值, 考虑故障点返回值, 是否有故障点的方案数
暴力转移即可
#include <bits/stdc++.h> using namespace std; const int N = 1e5+10, P = 1e9+7; int dp[N][8]; int n, L[N], R[N], val[N]; int nand(int a, int b, int v) { int ans = 3^(a&b), f = a>>2|b>>2; if (v==0) ans &= 1, f = 1; if (v==1) ans |= 2, f = 1; if (!f) { if (ans&1) ans |= 2; else ans &= 1; } return ans|f<<2; } void add(int &a, int64_t b) {a=(a+b)%P;} void dfs(int x) { if (!x) return; dfs(L[x]),dfs(R[x]); for (int u=0; u<8; ++u) for (int v=0; v<8; ++v) { add(dp[x][nand(u,v,val[x])],(int64_t)dp[L[x]][u]*dp[R[x]][v]); } } int main() { dp[0][0] = dp[0][3] = 1; scanf("%d", &n); for (int i=1; i<=n; ++i) scanf("%d%d%d", &L[i], &R[i], &val[i]); dfs(1); printf("%d ", (dp[1][5]+dp[1][6])%P); }
H 签到
I (kruskal)重构树求瓶颈路
#include <bits/stdc++.h> using namespace std; const int N = 2e5+10; int n, r, fa[N], val[N], sz[N], dep[N]; int son[N], top[N]; map<pair<int,int>,int> E; struct edge { int u, v, w; bool operator < (const edge &rhs) const { return w<rhs.w; } } e[N]; vector<int> g[N]; int Find(int x) {return fa[x]?fa[x]=Find(fa[x]):x;} void dfs(int x, int f, int d) { sz[x] = 1, fa[x] = f, dep[x] = d; for (int y:g[x]) if (y!=f) { dfs(y,x,d+1); sz[x] += sz[y]; if (sz[y]>sz[son[x]]) son[x] = y; } } void dfs(int x, int tf) { top[x] = tf; if (son[x]) dfs(son[x],tf); for (int y:g[x]) if (!top[y]) dfs(y,y); } int lca(int x, int y) { while (top[x]!=top[y]) { if (dep[top[x]]<dep[top[y]]) y = fa[y]; else x = fa[x]; } return dep[x]<dep[y]?x:y; } int main() { scanf("%d%d", &n, &r); for (int i=1; i<=r; ++i) { int u, v, w; scanf("%d%d%d", &u, &v, &w); E[{u,v}] = w; e[i] = {u,v,w}; } sort(e+1,e+1+r); int tot = n, sum = 0; for (int i=1; i<=r; ++i) { int u = Find(e[i].u), v = Find(e[i].v); if (u!=v) { ++tot; fa[u] = fa[v] = tot; g[tot].push_back(u); g[tot].push_back(v); sum += val[tot] = e[i].w; } } dfs(tot,0,0),dfs(tot,tot); int q; scanf("%d", &q); while (q--) { int u, v; scanf("%d%d", &u, &v); printf("%d ", sum-val[lca(u,v)]+E[{u,v}]); } }
J 暴力检验因子
#include <bits/stdc++.h> using namespace std; int cnt[100010],vis[100010]; char s[100010]; int main() { scanf("%s", s); int n = strlen(s); auto gao = [&](int x) { for (int i=0; i<x; ++i) cnt[i] = 0; for (int i=0; i<n; ++i) if (s[i]=='P') ++cnt[i%x]; for (int i=0; i<x; ++i) if (cnt[i]==0) vis[x] = 1; }; for (int i=1; i<=n; ++i) if (n%i==0) gao(i),gao(n/i); int ans = 0; for (int i=1; i<n; ++i) if (vis[i]) { ++ans; for (int j=i; j<n; j+=i) vis[j] = 1; } printf("%d ", ans); }
K 'o'向外提供1个度数, '-'提供两个度数, 建二分图看是否满流即可
#include <bits/stdc++.h> using namespace std; const int N = 1e6+10, S = N-2, T = N-1, INF = 0x3f3f3f3f; int n, m; struct edge { int to,w,next; edge(int to=0,int w=0,int next=0):to(to),w(w),next(next){} } e[N]; int head[N], dep[N], vis[N], cur[N], cnt=1; queue<int> Q; int bfs() { for (int i=1;i<=n*m;++i) dep[i]=INF,vis[i]=0,cur[i]=head[i]; dep[S]=INF,vis[S]=0,cur[S]=head[S]; dep[T]=INF,vis[T]=0,cur[T]=head[T]; dep[S]=0,Q.push(S); while (Q.size()) { int u = Q.front(); Q.pop(); for (int i=head[u]; i; i=e[i].next) { if (dep[e[i].to]>dep[u]+1&&e[i].w) { dep[e[i].to]=dep[u]+1; Q.push(e[i].to); } } } return dep[T]!=INF; } int dfs(int x, int w) { if (x==T) return w; int used = 0; for (int i=cur[x]; i; i=e[i].next) { cur[x] = i; if (dep[e[i].to]==dep[x]+1&&e[i].w) { int f = dfs(e[i].to,min(w-used,e[i].w)); if (f) used+=f,e[i].w-=f,e[i^1].w+=f; if (used==w) break; } } return used; } int dinic() { int ans = 0; while (bfs()) ans+=dfs(S,INF); return ans; } void add(int u, int v, int w) { e[++cnt] = edge(v,w,head[u]); head[u] = cnt; e[++cnt] = edge(u,0,head[v]); head[v] = cnt; } char s[22][22]; const int dx[]={0,0,-1,1}; const int dy[]={-1,1,0,0}; int main() { scanf("%d%d", &n, &m); for (int i=0; i<n; ++i) scanf("%s", s[i]); int sum = 0; for (int i=0; i<n; ++i) { for (int j=0; j<m; ++j) { int w = s[i][j]=='o'?1:2; sum += w; if (i+j&1) add(S,i*m+j+1,w); else add(i*m+j+1,T,w); for (int k=0; k<4; ++k) { int ii=i+dx[k],jj=j+dy[k]; if (ii<0||jj<0||ii>=n||jj>=m) continue; if (i+j&1) add(i*m+j+1,ii*m+jj+1,w); else add(ii*m+jj+1,i*m+j+1,w); } } } puts(dinic()*2==sum?"Y":"N"); }
L
M 预处理SA, 每一步贪心取后缀最小的
#include <bits/stdc++.h> using namespace std; const int N = 1e6+10, P = 1e9+7; struct SA { int n,c[N],rk[N],h[N],sa[N],a[N]; void build(int m) { a[n+1] = rk[n+1] = h[n+1] = 0; int i,*x=rk,*y=h; for(i=1;i<=m;i++) c[i]=0; for(i=1;i<=n;i++) c[x[i]=a[i]]++; for(i=1;i<=m;i++) c[i]+=c[i-1]; for(i=n;i;i--) sa[c[x[i]]--]=i; for(int k=1,p;k<=n;k<<=1) { p=0; for(i=n-k+1;i<=n;i++) y[++p]=i; for(i=1;i<=n;i++) if(sa[i]>k) y[++p]=sa[i]-k; for(i=1;i<=m;i++) c[i]=0; for(i=1;i<=n;i++) c[x[y[i]]]++; for(i=1;i<=m;i++) c[i]+=c[i-1]; for(i=n;i;i--) sa[c[x[y[i]]]--]=y[i]; swap(x,y); x[sa[1]]=1; p=1; for(i=2;i<=n;i++) x[sa[i]]=(y[sa[i-1]]==y[sa[i]] && y[sa[i-1]+k]==y[sa[i]+k])?p:++p; if(p==n) break; m=p; } for(i=1;i<=n;i++) rk[sa[i]]=i; for(int i=1,j,k=0;i<=n;i++) if (rk[i]!=1) { if(k) k--; j=sa[rk[i]-1]; while(a[i+k]==a[j+k]) k++; h[rk[i]] = k; } } } sa; int vis[N],pw[N]; int main() { int n, cnt = 0; scanf("%d", &n); for (int i=0; i<n; ++i) { int k, x; scanf("%d", &k); vis[sa.n+1] = 1; cnt += k; while (k--) { scanf("%d", &x); sa.a[++sa.n] = x; } sa.a[++sa.n] = 500; } sa.build(500); priority_queue<int,vector<int>,greater<int>> q; for (int i=1; i<=sa.n; ++i) if (vis[i]) q.push(sa.rk[i]); int ans = 0; for (int i=cnt; i; --i) { int p = sa.sa[q.top()]; q.pop(); ans = (ans*365ll+sa.a[p])%P; if (p+1!=sa.n&&sa.a[p+1]!=500) q.push(sa.rk[p+1]); } ans = ans*365ll%P; printf("%d ", ans); }