The topic is relatively simple this time.
The Tom and Jerry Game!
slightly
Operations on a Tuple
slightly
The Delicious Cake
slightly
Convenient Airports
Note that the lower bound of the answer is 2 ⋅ Max (N − M − 1, ⌈ d02 ⌉) 2 \ cdot \ max(N-M-1, ⌈ lceil \ frac {D_ 0} {2} \ rceil) 2 ⋅ max(N − M − 1, ⌈ 2d0 ⌉), where d0d_0d0 is the number of points with degree of 000. Here is a construction that can reach this lower bound.
For two connected blocks with internal edges, we take one edge (U1, V1) (U) from the first connected block_ 1,v_ 1) (U1, V1), take an edge (U2, V2) (U) from the second connected block_ 2,v_ 2)(u2,v2). If (u1,v1)(u_1,v_1)(u1, V1) and (U2, V2) (U_ 2,v_ 2) At least one edge in (U2, V2) is not the cut edge of the corresponding connected block. We can do one operation: Delete (u1,v1)(u_1,v_1)(u1, V1) and (u2,v2)(u_2,v_2)(u2, V2), add (u1,v2)(u_1,v_2)(u1, V2) and (u2,v1)(u_2,v_1)(u2,v1). This does not change the degree of any point, and can merge two connected blocks.
Then we can describe our algorithm: at the beginning, there is a space connected block SSS. First, we use the above operations to merge the SSS and all the connected blocks with rings (obviously, we can do it). Then we consider merging the SSS and all the connected blocks without rings but not single points. If the SSS has non cutting edges at this time, we can continue to operate. Otherwise, the whole graph is already a forest, and we need to add one more edge. Finally, consider merging SSS with all single points of degree 000. If SSS has non cutting edge (u1,v1)(u_1,v_1)(u1, v1) and at least two such single points u2u_2u2 and v2v_2v2, you can delete (u1,v1)(u_1,v_1)(u1, v1), add (u1,v2)(u_1,v_2)(u1, V2) and (u2,v1)(u_2,v_1)(u2, v1), so that only one edge can be added to merge two single points, otherwise only one edge can be added to merge one single point at a time. It is easy to prove that this algorithm can reach the lower bound given above.
When implementing, you can consider using queues to store all the edges in the current SSS that are not on the DFS tree. These edges are obviously non cutting edges and can be deleted at will. You can merge one connected block DFS at a time.
The time complexity of a single group of data is O (n + m) / mathcal o (n + m) O (n + m).
#include <bits/stdc++.h> #define last last2 using namespace std; struct Edge { int t,next; Edge() {} Edge(int a,int b):t(a),next(b) {} }; Edge e[1000005]; int head[100005],q[1000005],l,r,tot; bool vis1[100005],vis2[1000005],vis3[100005]; inline void addEdge(int x,int y) { e[++tot]=Edge(y,head[x]); head[x]=tot; e[++tot]=Edge(x,head[y]); head[y]=tot; } int id[100005],s1,s2; bool kind[100005]; void dfs1(int x) { s1++; for(int i=head[x];i;i=e[i].next) { s2++; int u=e[i].t; if (!id[u]) { id[u]=id[x]; dfs1(u); } } } int dep[100005]; void dfs2(int x) { vis1[x]=1; for(int i=head[x];i;i=e[i].next) if (id[e[i].t]==id[x]&&!vis2[(i+1)>>1]) { int u=e[i].t; if (!vis1[u]) { dep[u]=dep[x]+1; dfs2(u); } else if (dep[u]>dep[x]) q[++r]=((i+1)>>1); } } int main() { int cases; scanf("%d",&cases); for(;cases;cases--) { memset(head,0,sizeof(head)); memset(vis1,0,sizeof(vis1)); memset(vis2,0,sizeof(vis2)); memset(vis3,0,sizeof(vis3)); memset(id,0,sizeof(id)); int n,m; scanf("%d%d",&n,&m); tot=0; for(int i=1;i<=m;i++) { int x,y; scanf("%d%d",&x,&y); addEdge(x,y); } int s0=0,cnt=0; for(int i=1;i<=n;i++) if (!id[i]) { if (!head[i]) s0++; id[i]=++cnt; s1=s2=0; dfs1(i); kind[cnt]=(s1<=(s2>>1)); } printf("%d ",max(2*(n-1-m),2*((s0+1)>>1))); l=1;r=0; int rt=0; for(int i=1;i<=n;i++) if (!vis3[id[i]]&&kind[id[i]]) { vis3[id[i]]=1; if (l<=r) { int t1=q[l++],t2=((head[i]+1)>>1); vis2[t1]=vis2[t2]=1; int u1=e[2*t1-1].t,v1=e[2*t1].t; int u2=e[2*t2-1].t,v2=e[2*t2].t; addEdge(u1,u2); addEdge(v1,v2); dep[u2]=0; dfs2(u2); if (vis1[v2]) q[++r]=(tot>>1); else { dep[v2]=0; dfs2(v2); } } else { dep[i]=0; dfs2(i); rt=i; } } for(int i=1;i<=n;i++) if (!vis3[id[i]]&&head[i]) { vis3[id[i]]=1; if (l<=r) { int t1=q[l++],t2=((head[i]+1)>>1); vis2[t1]=vis2[t2]=1; int u1=e[2*t1-1].t,v1=e[2*t1].t; int u2=e[2*t2-1].t,v2=e[2*t2].t; addEdge(u1,u2); addEdge(v1,v2); } else if (rt) addEdge(rt,i); else rt=i; } int last=0; for(int i=1;i<=n;i++) if (!vis3[id[i]]&&!head[i]) { vis3[id[i]]=1; if (l<=r) { if (last) { int t=q[l++]; vis2[t]=1; int u=e[2*t-1].t,v=e[2*t].t; addEdge(u,last); addEdge(v,i); last=0; } else last=i; } else addEdge(rt,i); } if (last) addEdge(rt,last); int s=0; for(int i=1;i<=(tot>>1);i++) if (!vis2[i]) s++; printf("%d\n",s); for(int i=1;i<=(tot>>1);i++) if (!vis2[i]) printf("%d %d\n",e[2*i].t,e[2*i-1].t); } return 0; }
Guessing Game
It may be the most difficult topic in this month's competition. It is not difficult to get the algorithm of O(log n) - mathcal o (\ log n) O (logn) times query, but it needs further analysis and discussion to pass the limit of K=120K=120K=120.
Consider a more violent algorithm first. Maintain a possible set of current answers. When the size of the set is constant, brute force query can be performed. If 'E' E 'is returned at any time, it can be terminated directly. Otherwise, find the number aaa of the position of set 12\frac{1}{2}21 every time and ask aaa. It is obvious that G 'g' and L 'L' are equivalent here. Let's assume that G 'g' is returned. Ask the number b B b of the position of the set 14\frac{1}{4}41 again, and then return 'g' g 'g' which means that at least one of the two queries returns information is true, then obviously ≤ b \ Leq The number of b ≤ b can't be SSS, which can be deleted. Returning 'L' 'L' means that the two messages are conflicting, and at least one of the two queries returns is true, then the number between b ∼ ab\sim ab ∼ a must not be SSS. In either case, the set size can be reduced by at least 14\frac{1}{4}41, which requires about 2 ⋅ log 43n+O(1) ≈ 1442 \ cdot \ log_ {\ frac {4} {3} n + \ mathcal o (1) \ approx 1442 ⋅ log34 n+O(1) ≈ 144 times, unable to pass.
Optimize the algorithm. Note that in the second case above, we don't make full use of the information. At this time, if we ask again in the part of ≥ a\geq a ≥ a, because bbb returned 'L' L 'last time, whatever we returned this time will be deleted. Consider changing bbb to the number of positions of set 13\frac{1}{3}31, so that if you return 'G' G ', you can reduce the size of the set by at least 13\frac{1}{3}31. Otherwise, the number c c c of the location of 34\frac{3}{4}43 can be asked again, and the number between b ∼ cb\sim cb ∼ C can be deleted if 'G ′' G 'is returned, and the number between b ∼ ab\sim ab ∼ A and ≥ c\geq c ≥ C can be deleted if' L ′ 'L' is returned. In either case, the aggregation size can be reduced by at least 512\frac{5}{12}125. In this way, each round can be reduced by 13\frac{1}{3}31 with 222 operations, or 512\frac{5}{12}125 with 333 operations. It needs Max (2 ⋅ log 32n,3 ⋅ log 127n)+O(1) ≈ 115 \ max (2 \ cdot \ log)_ {\frac{3}{2}}n,3\cdot \log_ {\ frac {12} {7} n) + \ mathcal o (1) \ approx 115max (2 ⋅ log23 n, 3 ⋅ log712 n)+O(1) ≈ 115 times, which can be passed. 13\frac{1}{3}31 can also be improved to a more accurate constant by dichotomy, but the optimization is not great.
During implementation, it is necessary to maintain the set of possible answers. At any time, it is O(log n) - mathcal o (\ log n) O(log n) O(log n) continuous interval and the time complexity of violent maintenance is O(log 2n) - mathcal o (\ log ^ 2n) O(log 2n).
#include <bits/stdc++.h> #define FR first #define SE second using namespace std; typedef long long ll; typedef pair<int,int> pr; int ans,k; bool ask(int x) { printf("%d\n",x); fflush(stdout); char str[5]; scanf("%s",str); if (str[0]=='E') exit(0); return str[0]=='G'; /*k++; if (x==ans) { printf("%d %d\n",ans,k); exit(0); } if (k&1) return ans>x; else return (ans>x)^(rand()%2);*/ } pr a[1005]; int sz; int erase(int l,int r) { static pr cur[1005]; int nsz=0; for(int i=1;i<=sz;i++) if (a[i].FR>r||a[i].SE<l) cur[++nsz]=a[i]; else { if (a[i].FR<l) cur[++nsz]=pr(a[i].FR,l-1); if (a[i].SE>r) cur[++nsz]=pr(r+1,a[i].SE); } sz=nsz; int s=0; for(int i=1;i<=sz;i++) { a[i]=cur[i]; s+=a[i].SE-a[i].FR+1; } return s; } int query(int k) { for(int i=1;i<=sz;i++) { int v=a[i].SE-a[i].FR+1; if (k>v) k-=v; else return a[i].FR+k-1; } } int randint() { return ((ll)rand()*9116111716LL+rand())%1000000007; } int main() { int n; scanf("%d",&n); //ans=randint()%n+1; sz=1; a[1]=pr(1,n); while (n>3) { int mid=query((n+1)>>1); if (!ask(mid)) { int r=query(n-n/3+1); if (!ask(r)) n=erase(r,1e9); else { int l=query(n/4); if (!ask(l)) n=erase(l,r); else { n=erase(1,l); n=erase(mid,r); } } } else { int l=query(n/3); if (ask(l)) n=erase(1,l); else { int r=query(n-n/4+1); if (ask(r)) n=erase(l,r); else { n=erase(r,1e9); n=erase(l,mid); } } } } for(int i=1;i<=sz;i++) for(int j=a[i].FR;j<=a[i].SE;j++) ask(j); return 0; }
Danya and Different Values
The ccc of each color is considered separately. For each point XXX, we obviously only need to know the minimum depth of ccc in the XXX subtree (not exist as inf \ infinf). Then for the virtual tree composed of points of ccc color, the minimum depth of the corresponding subtree of each point on the virtual tree is the same (the minimum depth of the subtree not on these chains is inf \ infinf, not to be considered). In this way, we can make a simple difference to divide all the changes corresponding to the colors into o (n) and mathcal o (n) O (n) group (u,v,w)(u,v,w)(u,v,w), which means that XXX is the point on the path from uuu to the root, and depx+D ≥ vdep_x+D\geq vdepx + D ≥ v will contribute to www.
Then the line segment tree is built for the depth, and the line segment tree of the subtree can be merged directly. The time complexity of a single group of data is O((N+Q)log n) - mathcal o ((n + Q) - log n) O ((n + Q) logn).
#include <bits/stdc++.h> #define inf 0x3f3f3f3f using namespace std; namespace SGT { const int Maxn=20000000; int ch[Maxn][2],sumv[Maxn],tot; inline int cpynode(int o) { tot++; ch[tot][0]=ch[o][0];ch[tot][1]=ch[o][1]; sumv[tot]=sumv[o]; return tot; } int update(int l,int r,int o,int p,int q) { o=cpynode(o); sumv[o]+=q; if (l==r) return o; else { int m=((l+r)>>1); if (m>=p) ch[o][0]=update(l,m,ch[o][0],p,q); else ch[o][1]=update(m+1,r,ch[o][1],p,q); return o; } } int merge(int l,int r,int x,int y) { if (!x) return y; if (!y) return x; int o=++tot; sumv[o]=sumv[x]+sumv[y]; if (l==r) return o; else { int m=((l+r)>>1); ch[o][0]=merge(l,m,ch[x][0],ch[y][0]); ch[o][1]=merge(m+1,r,ch[x][1],ch[y][1]); return o; } } int query(int l,int r,int o,int p) { if (!o) return 0; if (l==r) return sumv[o]; else { int m=((l+r)>>1); if (m>=p) return query(l,m,ch[o][0],p); else return sumv[ch[o][0]]+query(m+1,r,ch[o][1],p); } } } vector <int> e1[200005],vt1[200005]; int num[200005]; int dep[200005],fa[200005][20]; int dfn[200005],dfs_cnt; void dfs1(int x) { dep[x]=dep[fa[x][0]]+1; dfn[x]=++dfs_cnt; for(int i=0;i<e1[x].size();i++) { int u=e1[x][i]; fa[u][0]=x; for(int j=1;j<20;j++) fa[u][j]=fa[fa[u][j-1]][j-1]; dfs1(u); } } int lca(int x,int y) { if (dep[x]<dep[y]) swap(x,y); int d=dep[x]-dep[y]; for(int i=0;i<20;i++) if ((d>>i)&1) x=fa[x][i]; if (x==y) return x; for(int i=19;i>=0;i--) if (fa[x][i]!=fa[y][i]) { x=fa[x][i]; y=fa[y][i]; } return fa[x][0]; } vector <int> e2[200005],vt2[200005]; int minn[200005],st[200005]; void dfs2(int x,int v) { minn[x]=inf; int id=0; if (num[x]==v) { vt2[x].push_back(dep[x]); minn[x]=dep[x]; id=x; } for(int i=0;i<e2[x].size();i++) { int u=e2[x][i]; dfs2(u,v); if (minn[u]<minn[x]) { minn[x]=minn[u]; id=u; } } for(int i=0;i<e2[x].size();i++) if (e2[x][i]!=id) vt2[x].push_back(-minn[e2[x][i]]); e2[x].clear(); } bool cmp(int x,int y) { return dfn[x]<dfn[y]; } void build(int v) { if (!vt1[v].size()) return; sort(vt1[v].begin(),vt1[v].end(),cmp); int top=1; st[1]=1; for(int i=0;i<vt1[v].size();i++) if (st[top]!=vt1[v][i]) { int x=vt1[v][i]; int p=lca(x,st[top]); for(;;) { if (dep[p]<=dep[st[top-1]]) { e2[st[top-1]].push_back(st[top]); top--; } else if (dep[p]<dep[st[top]]) { e2[p].push_back(st[top]); st[top]=p; } else break; } if (st[top]!=x) st[++top]=x; } for(;top>1;top--) e2[st[top-1]].push_back(st[top]); dfs2(1,v); } int root[200005]; void dfs3(int x,int n) { root[x]=0; for(int i=0;i<vt2[x].size();i++) { int u=vt2[x][i]; root[x]=SGT::update(1,n,root[x],abs(u),((u>0)?1:-1)); } for(int i=0;i<e1[x].size();i++) { int u=e1[x][i]; dfs3(u,n); root[x]=SGT::merge(1,n,root[x],root[u]); } } int main() { minn[0]=inf; int cases; scanf("%d",&cases); for(;cases;cases--) { SGT::tot=0; int n; scanf("%d",&n); for(int i=1;i<=n;i++) { e1[i].clear(); vt1[i].clear(); vt2[i].clear(); } for(int i=2;i<=n;i++) { int x; scanf("%d",&x); e1[x].push_back(i); } for(int i=1;i<=n;i++) { scanf("%d",&num[i]); vt1[num[i]].push_back(i); } dfs_cnt=0; dfs1(1); for(int i=1;i<=n;i++) build(i); dfs3(1,n); int m; scanf("%d",&m); int lastans=0; for(int i=1;i<=m;i++) { int x,y; scanf("%d%d",&x,&y); x^=lastans;y^=lastans; y=min(n,dep[x]+y); printf("%d\n",lastans=SGT::query(1,n,root[x],y)); } } return 0; }
Perfect Partitions
A very routine topic.
It is easy to find that we actually require F(x) = Π i=1Q(∑ J ≤ bixai ⋅ j)F(x)=\prod_{i=1}^{Q}(\sum_{j\leq b_i}x^{a_i\cdot j})F(x) = Πi=1Q(∑ J ≤ bi xai ⋅ j) coefficient of the first N+1N+1N+1 term.
Consider the routine of taking ln and then exp and exp, LN F(x) = ∑ i=1Q(ln (1 − xai ⋅ bi+1) − ln (1 − xia))\ln F(x)=\sum_{i=1}^{Q}(\ln(1-x^{a_i \cdot{b_i+1}})-\ln (1-x^a_i))lnF(x) = ∑ i=1Q(ln (1 − xai ⋅ bi+1) − ln (1 − xia)), that is, the sum of several ln (1 − x k) / ln (1-x ^ k) ln (1 − xk) band coefficients. N) O (nlogn).
After calculating the coefficients of N+1N+1N+1 before LNF (x), the coefficients of N+1N+1N+1 before exp (LNF (x)) \ exp (\ LNF (x)) exp (LNF (x)) can be calculated directly. The time complexity is O(Nlog n + Q) / mathcal o (n \ log n + Q) O(Nlog n + Q).
#include <bits/stdc++.h> #define MOD 998244353 using namespace std; typedef long long ll; ll pow_mod(ll x,int k) { ll ans=1; while (k) { if (k&1) ans=ans*x%MOD; x=x*x%MOD; k>>=1; } return ans; } const int Maxn=1<<19; ll *w[19]; void ntt_init() { for(int i=2,t=0;i<=Maxn;i<<=1,t++) { w[t]=new ll[i>>1]; ll wn=pow_mod(3,(MOD-1)/i); w[t][0]=1; for(int j=1;j<(i>>1);j++) w[t][j]=w[t][j-1]*wn%MOD; } } ll inv[Maxn]; void pre(int n) { inv[1]=1; for(int i=2;i<=n;i++) inv[i]=(MOD-MOD/i)*inv[MOD%i]%MOD; } void rev(ll *p,int len) { int j=len>>1; for(int i=1;i<len-1;i++) { if (i<j) swap(p[i],p[j]); int k=len>>1; while (j>=k) { j-=k; k>>=1; } if (j<k) j+=k; } } void ntt(ll *p,int len,int check) { rev(p,len); for(int i=2,t=0;i<=len;i<<=1,t++) for(int j=0;j<len;j+=i) for(int k=j;k<j+(i>>1);k++) { ll u=p[k]; ll v=w[t][k-j]*p[k+(i>>1)]; p[k]=(u+v)%MOD; p[k+(i>>1)]=(u-v)%MOD; } if (check==-1) { reverse(p+1,p+len); ll nev=pow_mod(len,MOD-2); for(int i=0;i<len;i++) p[i]=(p[i]+MOD)*nev%MOD; } } void getinv(ll *p,ll *q,int len) { static ll t1[Maxn]; if (len==1) { q[0]=1; return; } getinv(p,q,len>>1); memcpy(t1,p,sizeof(ll)*len); memset(t1+len,0,sizeof(ll)*len); ntt(q,len<<1,1); ntt(t1,len<<1,1); for(int i=0;i<(len<<1);i++) q[i]=q[i]*(2LL-q[i]*t1[i]%MOD)%MOD; ntt(q,len<<1,-1); memset(q+len,0,sizeof(ll)*len); } void getdif(ll *p,ll *q,int len) { for(int i=0;i<len-1;i++) q[i]=p[i+1]*(i+1)%MOD; q[len-1]=0; } void getint(ll *p,ll *q,int len) { for(int i=len-1;i>0;i--) q[i]=p[i-1]*inv[i]%MOD; q[0]=0; } void getln(ll *p,ll *q,int len) { static ll t1[Maxn]; memset(t1,0,sizeof(ll)*(len<<1)); getinv(p,t1,len); getdif(p,q,len); ntt(q,len<<1,1); ntt(t1,len<<1,1); for(int i=0;i<(len<<1);i++) q[i]=q[i]*t1[i]%MOD; ntt(q,len<<1,-1); getint(q,q,len); memset(q+len,0,sizeof(ll)*len); } void getexp(ll *p,ll *q,int len) { static ll t1[Maxn]; if (len==1) { q[0]=1; return; } getexp(p,q,len>>1); memset(t1,0,sizeof(ll)*(len<<1)); getln(q,t1,len); for(int i=0;i<len;i++) t1[i]=(p[i]-t1[i]+MOD)%MOD; t1[0]=(t1[0]+1LL)%MOD; ntt(q,len<<1,1); ntt(t1,len<<1,1); for(int i=0;i<(len<<1);i++) q[i]=q[i]*t1[i]%MOD; ntt(q,len<<1,-1); memset(q+len,0,sizeof(ll)*len); } ll p[Maxn],q[Maxn]; ll cnt[200005]; int main() { ntt_init(); int n,m; scanf("%d%d",&n,&m); int len=1; while (len<=n) len<<=1; pre(len); for(int i=1;i<=m;i++) { int x,y; scanf("%d%d",&x,&y); cnt[x]=(cnt[x]+1LL)%MOD; if ((ll)(y+1)*x<=n) cnt[(y+1)*x]=(cnt[(y+1)*x]-1LL+MOD)%MOD; } for(int i=1;i<=n;i++) if (cnt[i]) { for(int j=1;i*j<len;j++) p[i*j]=(p[i*j]+cnt[i]*inv[j])%MOD; } getexp(p,q,len); for(int i=1;i<=n;i++) printf("%lld ",q[i]); printf("\n"); return 0; }