2020 CCPC Qinhuangdao Solutions
Problem A
Solution:
很简单的组合计数公式题,答案是:$\frac{\binom{r}{2}}{\binom{r+b}{2}}$。
1 #include "bits/stdc++.h" 2 #define rep(i,a,n) for(int i=a;i<=n;i++) 3 #define per(i,a,n) for(int i=n;i>=a;i--) 4 #define pb push_back 5 #define mp make_pair 6 #define FI first 7 #define SE second 8 #define maxn 100000 9 #define mod 1000000007 10 #define inf 0x3f3f3f3f 11 using namespace std; 12 typedef long long ll; 13 typedef pair<int,int> pii; 14 typedef vector<int> vi; 15 typedef double db; 16 17 int main() 18 { 19 int CAS; scanf("%d",&CAS); rep(cas,1,CAS) 20 { 21 int r,b; scanf("%d%d",&r,&b); 22 int p=r*(r-1)/2,q=(r+b)*(r+b-1)/2; 23 int g=__gcd(p,q); 24 printf("Case #%d: %d/%d\n",cas,p/g,q/g); 25 } 26 return 0; 27 }
Problem B
Solution:
不失一般性,我们只用考虑查询操作中的点 $p = (x,y)$ 在最优长方形上下左右四条边中的下面的那条边的情况,即最优长方形上下行号满足 $x_1 \le x_2 = x$。(剩下三种情况可以通过将输入旋转90度/180度/270度来处理,对4种情况的4个答案取最大值即为原问题答案。)枚举 $x_1$,通过预处理的 $lft[x_1][y]$ 和 $rt[x_1][y]$ 可以 $O(1)$ 时间确定 $x_1$ 行内包含 $(x_1,y)$ 的最大区间。同样我们可以通过 $lft[x][y]$ 和 $rt[x][y]$ 知道 $x$ 行内包含 $(x,y)$的最大区间。这两个区间取交集后得到区间 $[l,r]$,我们只需要快速计算 $\{ j | (i,j) 是干地, \forall i \in [x_1+1, x-1]\}$ 中的最大值和最小值即可。发现预处理出每个点上方的湿地首次出现位置后,倒序枚举 $x_1$ 并用并查集维护湿地形成的区间可以用 $O(\alpha(n))$ 完成询问。对于修改操作,重新计算 $p = (x,y)$ 所在行列的预处理的数组即可。
1 #include "bits/stdc++.h" 2 #define rep(i,a,n) for(int i=a;i<=n;i++) 3 #define per(i,a,n) for(int i=n;i>=a;i--) 4 #define pb push_back 5 #define mp make_pair 6 #define FI first 7 #define SE second 8 #define maxn 1000 9 #define mod 1000000007 10 #define inf 0x3f3f3f3f 11 using namespace std; 12 typedef long long ll; 13 typedef pair<int,int> pii; 14 typedef vector<int> vi; 15 typedef double db; 16 17 char S[maxn+5][maxn+5],s[maxn+5][maxn+5]; 18 pair<int,pii> OP[maxn+5],op[maxn+5]; 19 int ans[maxn+5]; 20 21 int pre[maxn+5][maxn+5],lft[maxn+5][maxn+5],rt[maxn+5][maxn+5]; 22 vi pool[maxn+5]; 23 bool mark[maxn+5]; 24 int fa[maxn+5],mx[maxn+5],mn[maxn+5]; 25 int getfa(int x) 26 { 27 if(fa[x]==x) return x; 28 return fa[x]=getfa(fa[x]); 29 } 30 31 32 void mrg(int x,int y) 33 { 34 int fx=getfa(x); 35 int fy=getfa(y); 36 fa[fy]=fx; 37 mn[fx]=min(mn[fx],mn[fy]); 38 mx[fx]=max(mx[fx],mx[fy]); 39 } 40 41 void solve(int n,int m,int Q) 42 { 43 rep(i,1,n) rep(j,1,m) pre[i][j]=s[i][j]=='.'?i:pre[i-1][j]; 44 rep(i,1,n) rep(j,1,m) lft[i][j]=s[i][j]=='.'?j:lft[i][j-1]; 45 rep(i,1,n) per(j,1,m) rt[i][j]=s[i][j]=='.'?j:(j==m?m+1:rt[i][j+1]); 46 rep(q,1,Q) 47 { 48 int x=op[q].SE.FI; 49 int y=op[q].SE.SE; 50 if(op[q].FI==1) 51 { 52 s[x][y]=s[x][y]=='.'?'#':'.'; 53 rep(i,x,n) pre[i][y]=s[i][y]=='.'?i:pre[i-1][y]; 54 rep(j,y,m) lft[x][j]=s[x][j]=='.'?j:lft[x][j-1]; 55 per(j,1,y) rt[x][j]=s[x][j]=='.'?j:(j==m?m+1:rt[x][j+1]); 56 } 57 else 58 { 59 if(s[x][y]=='#') 60 { 61 int l=lft[x][y]+1,r=rt[x][y]-1; 62 ans[q]=max(ans[q],r-l+1); 63 rep(i,1,x) pool[i].clear(); 64 rep(j,l-1,r+1) mark[j]=0; 65 rep(j,l,r) pool[pre[x][j]].pb(j); 66 per(i,1,x-1) 67 { 68 int L=lft[i][y]+1,R=rt[i][y]-1; 69 L=max(L,l); R=min(R,r); 70 if(L<=y && y<=R) 71 { 72 if(mark[L]) 73 { 74 int f=getfa(L); 75 L=mx[f]+1; 76 } 77 if(mark[R]) 78 { 79 int f=getfa(R); 80 R=mn[f]-1; 81 } 82 if(L<=y && y<=R) ans[q]=max(ans[q],(x-i+1)*(R-L+1)); 83 } 84 for(auto j: pool[i]) 85 { 86 mark[j]=1; fa[j]=mx[j]=mn[j]=j; 87 if(mark[j-1]) mrg(j-1,j); 88 if(mark[j+1]) mrg(j,j+1); 89 } 90 } 91 } 92 } 93 } 94 } 95 96 int main() 97 { 98 int CAS; scanf("%d",&CAS); rep(cas,1,CAS) 99 { 100 int n,m,Q; scanf("%d%d%d",&n,&m,&Q); 101 rep(i,1,n) scanf("%s",S[i]+1); 102 rep(q,1,Q) scanf("%d%d%d",&OP[q].FI,&OP[q].SE.FI,&OP[q].SE.SE); 103 rep(q,1,Q) ans[q]=0; 104 105 rep(i,1,n) rep(j,1,m) s[i][j]=S[i][j]; 106 rep(q,1,Q) op[q]=mp(OP[q].FI,mp(OP[q].SE.FI,OP[q].SE.SE)); 107 solve(n,m,Q); 108 109 rep(i,1,n) rep(j,1,m) s[j][n-i+1]=S[i][j]; 110 rep(q,1,Q) op[q]=mp(OP[q].FI,mp(OP[q].SE.SE,n-OP[q].SE.FI+1)); 111 solve(m,n,Q); 112 113 rep(i,1,n) rep(j,1,m) s[n-i+1][m-j+1]=S[i][j]; 114 rep(q,1,Q) op[q]=mp(OP[q].FI,mp(n-OP[q].SE.FI+1,m-OP[q].SE.SE+1)); 115 solve(n,m,Q); 116 117 rep(i,1,n) rep(j,1,m) s[m-j+1][i]=S[i][j]; 118 rep(q,1,Q) op[q]=mp(OP[q].FI,mp(m-OP[q].SE.SE+1,OP[q].SE.FI)); 119 solve(m,n,Q); 120 121 printf("Case #%d:\n",cas); 122 rep(q,1,Q) if(OP[q].FI==2) printf("%d\n",ans[q]); 123 } 124 return 0; 125 }
Problem C
Solution:
这是个假题。std考虑的情况是 Bob 在障碍物构成的凸包内。此时 Alex 一定是和 BoB 位置重叠才会最优。因此极角排序后扫一遍就行了。(我的代码是先求出射线和线段交,再在边框上排序。)
1 #include "bits/stdc++.h" 2 #define rep(i,a,n) for(int i=a;i<=n;i++) 3 #define per(i,a,n) for(int i=n;i>=a;i--) 4 #define pb push_back 5 #define mp make_pair 6 #define FI first 7 #define SE second 8 #define maxn 100000 9 #define mod 1000000007 10 #define inf 0x3f3f3f3f 11 using namespace std; 12 typedef long long ll; 13 typedef pair<int,int> pii; 14 typedef vector<int> vi; 15 typedef double db; 16 17 const db eps=1e-10; 18 int cmp(db x) {return (x>eps)-(x<-eps);} 19 struct P 20 { 21 db x,y; 22 P():x(0),y(0){} 23 P(db a,db b):x(a),y(b){} 24 void in() {scanf("%lf%lf",&x,&y);} 25 P operator +(const P &a) const {return P(x+a.x,y+a.y);} 26 P operator -(const P &a) const {return P(x-a.x,y-a.y);} 27 P operator *(const db &a) const {return P(x*a,y*a);} 28 P operator /(const db &a) const {return P(x/a,y/a);} 29 db norm() {return sqrt(x*x+y*y);} 30 }; 31 32 db dot(P a,P b) {return a.x*b.x+a.y*b.y;} 33 db cross(P a,P b) {return a.x*b.y-a.y*b.x;} 34 bool p_on_seg(P p,P s,P t) 35 { 36 return cmp(cross(p-s,t-s))==0 && cmp(dot(p-s,p-t))<=0; 37 } 38 39 struct L 40 { 41 P a,b; 42 L(){} 43 L(P x,P y):a(x),b(y){} 44 }; 45 46 bool l_cross(L l1,L l2,P &a) 47 { 48 if(!cmp(cross(l1.a-l1.b,l2.a-l2.b))) return 0; 49 db s1=cross(l1.a-l2.a,l2.b-l2.a); 50 db s2=cross(l1.b-l2.a,l2.b-l2.a); 51 a=(l1.b*s1-l1.a*s2)/(s1-s2); 52 return 1; 53 } 54 55 db c[maxn*4+5]; 56 57 int main() 58 { 59 int CAS; scanf("%d",&CAS); rep(cas,1,CAS) 60 { 61 int w,h; scanf("%d%d",&w,&h); 62 P cor[5]={P(0,0),P(w,0),P(w,h),P(0,h),P(0,0)}; 63 db base[4]={0,(db)w,(db)w+h,(db)w+h+w}; 64 P o; o.in(); 65 int tot=0; 66 int n; scanf("%d",&n); 67 rep(cc,1,n) 68 { 69 P p; p.in(); 70 rep(i,0,3) 71 { 72 P cr; 73 if(l_cross(L(o,p),L(cor[i],cor[i+1]),cr) && cmp(dot(p-o,cr-o))>0 && p_on_seg(cr,cor[i],cor[i+1])) 74 { 75 c[++tot]=base[i]+(cr-cor[i]).norm(); 76 } 77 } 78 } 79 sort(c+1,c+tot+1); 80 c[tot+1]=c[1]+2*(w+h); 81 db ans=0; 82 rep(i,1,tot) ans=max(ans,c[i+1]-c[i]); 83 printf("Case #%d: %.10f\n",cas,ans); 84 } 85 return 0; 86 }
Problem E
Solution:
枚举所有学生真正成绩的最大值 $x$,统计集合 $\{ i | a_i 和 b_i 中至少一个在 [p\%\cdot x,x]内\}$。我们可以从小到大枚举 $x$,这样可以用双端指针滑窗来统计。(代码是我脑抽写了个树状数组过的。)
1 #include "bits/stdc++.h" 2 #define rep(i,a,n) for(int i=a;i<=n;i++) 3 #define per(i,a,n) for(int i=n;i>=a;i--) 4 #define pb push_back 5 #define mp make_pair 6 #define FI first 7 #define SE second 8 #define maxn 400000 9 #define mod 1000000007 10 #define inf 0x3f3f3f3f 11 using namespace std; 12 typedef long long ll; 13 typedef pair<int,int> pii; 14 typedef vector<int> vi; 15 typedef double db; 16 17 struct BIT 18 { 19 int a[maxn+5],n; 20 void init(int N) 21 { 22 n=N; 23 rep(i,1,n) a[i]=0; 24 } 25 void add(int x,int v) 26 { 27 for(;x<=n;x+=x&-x) a[x]+=v; 28 } 29 int ask(int x) 30 { 31 int r=0; 32 for(;x;x-=x&-x) r+=a[x]; 33 return r; 34 } 35 }bit; 36 37 pii cj[maxn+5]; 38 39 int main() 40 { 41 int CAS; scanf("%d",&CAS); rep(cas,1,CAS) 42 { 43 int n,p; scanf("%d%d",&n,&p); 44 vi vec,sc; 45 int mn=0; 46 rep(i,1,n) 47 { 48 int a,b; scanf("%d%d",&a,&b); 49 cj[i]={a,b}; 50 vec.pb(a); vec.pb(b); 51 sc.pb(a); sc.pb(b); 52 mn=max(mn,b); 53 } 54 sort(sc.begin(),sc.end()); 55 sort(vec.begin(),vec.end()); 56 vec.erase(unique(vec.begin(),vec.end()),vec.end()); 57 sort(cj+1,cj+n+1); 58 int N=vec.size(),ptr=1; 59 bit.init(N); 60 int ans=0; 61 rep(i,0,N-1) if(vec[i]>=mn) 62 { 63 int a=vec[i],thr=(1ll*a*p+99)/100; 64 int tmp=0; 65 tmp+=upper_bound(sc.begin(),sc.end(),a)-lower_bound(sc.begin(),sc.end(),thr); 66 while(ptr<=n && cj[ptr].FI<=a) 67 { 68 int id=lower_bound(vec.begin(),vec.end(),cj[ptr].SE)-vec.begin()+1; 69 bit.add(id,1); ptr++; 70 } 71 int id=lower_bound(vec.begin(),vec.end(),thr)-vec.begin()+1; 72 tmp-=bit.ask(N)-bit.ask(id-1); 73 ans=max(ans,tmp); 74 } 75 printf("Case #%d: %d\n",cas,ans); 76 } 77 return 0; 78 }
Problem F
Solution:
发现对于一个连通块的最优解要么是一个点都不选,要么是所有点都选。所以DFS或者并查集一下就行了。
1 #include "bits/stdc++.h" 2 #define rep(i,a,n) for(int i=a;i<=n;i++) 3 #define per(i,a,n) for(int i=n;i>=a;i--) 4 #define pb push_back 5 #define mp make_pair 6 #define FI first 7 #define SE second 8 #define maxn 300000 9 #define mod 1000000007 10 #define inf 0x3f3f3f3f 11 using namespace std; 12 typedef long long ll; 13 typedef pair<int,int> pii; 14 typedef vector<int> vi; 15 typedef double db; 16 17 int fa[maxn+5],sz[maxn+5],deg[maxn+5]; 18 int getfa(int x) 19 { 20 if(x==fa[x]) return x; 21 return fa[x]=getfa(fa[x]); 22 } 23 24 int main() 25 { 26 int CAS; scanf("%d",&CAS); rep(cas,1,CAS) 27 { 28 int n,m; scanf("%d%d",&n,&m); 29 rep(i,1,n) fa[i]=i,sz[i]=1,deg[i]=0; 30 rep(i,1,m) 31 { 32 int x,y; scanf("%d%d",&x,&y); 33 int fx=getfa(x); 34 int fy=getfa(y); 35 if(fx!=fy) 36 { 37 if(sz[fx]<sz[fy]) swap(fx,fy); 38 sz[fx]+=sz[fy]; 39 deg[fx]+=deg[fy]; 40 fa[fy]=fx; 41 } 42 fx=getfa(x); 43 deg[fx]+=2; 44 } 45 int ans=0; 46 rep(i,1,n) if(getfa(i)==i) 47 { 48 if(deg[i]/2>sz[i]) ans+=deg[i]/2-sz[i]; 49 } 50 printf("Case #%d: %d\n",cas,ans); 51 } 52 return 0; 53 }
Problem G
Solution:
$k=1$ 直接输出 $n$。$k \ge 2$ 时枚举 $i \in [1, n^{1/k}]$,用快速幂可以 $O(\log k)$ 时间内算出 $i^k$ 和 $(i+1)^k$,从而计算出 $|\{x|\lfloor x^{1/k} \rfloor = i, x\le n, x \equiv 0 (\text{mod }i)\}|$。
1 #include "bits/stdc++.h" 2 #define rep(i,a,n) for(int i=a;i<=n;i++) 3 #define per(i,a,n) for(int i=n;i>=a;i--) 4 #define pb push_back 5 #define mp make_pair 6 #define FI first 7 #define SE second 8 #define maxn 100000 9 #define mod 1000000007 10 #define inf 0x3f3f3f3f 11 using namespace std; 12 typedef long long ll; 13 typedef pair<int,int> pii; 14 typedef vector<int> vi; 15 typedef double db; 16 17 ll qp(ll a,int k) 18 { 19 ll res=1; 20 while(k) 21 { 22 if(k&1) res=res*a; 23 a=a*a; 24 k>>=1; 25 } 26 return res; 27 } 28 29 int main() 30 { 31 int CAS; scanf("%d",&CAS); rep(cas,1,CAS) 32 { 33 int n,k; scanf("%d%d",&n,&k); 34 int ans=0; 35 if(k==1) ans=n; 36 else 37 { 38 rep(i,1,n) 39 { 40 ll low=qp(i,k); 41 ll upp=(db)k*log10(i+1)>=9.5?inf:qp(i+1,k); 42 upp=min(upp-1,(ll)n); 43 ans+=upp/i-(low-1)/i; 44 if(upp==n) break; 45 } 46 } 47 printf("Case #%d: %d\n",cas,ans); 48 } 49 return 0; 50 }
Problem H
Solution:
首先注意到一个常见的“拆贡献”套路:$cnt(a,t)^2 = |\{(i,j) | a_i = a_j = t, 1 \le i, j \le n\}|$。因此 \[\sum_{a \in S_n} cnt(a,t)^2 = \sum_{a \in S_n}|\{(i,j) | a_i = a_j = t, 1 \le i, j \le n\}| = \sum_{1\le i,j \le n} |\{a \in S_n | a_i = a_j = t\}| = \sum_{1\le i\le n} |\{a \in S_n | a_i = t\}| + 2 \sum_{1\le i<j \le n} |\{a \in S_n | a_i = a_j = t\}|。\] 我们来说一下对任意一个 $i$,$|\{a \in S_n | a_i = t\}|$ 要怎么算。分两类情况来处理:
1. $\forall j < i$, $a_j < t$。令 $p$ 为 $a$ 的前缀最大值序列,那么我们知道 $p_{i-1} = t - 1$。令 $dp_1(i,x)$ 表示 $|\{(a_1, ...,a_i) | p_j-p_{j-1} \le 1, j \in [1, i], p_i = x\}|$,$dp_2(i,x)$ 表示 $|\{(a_1, ...,a_i) | p'_j-p'_{j-1} \le 1, j \in [1, i]\}|$,其中对于$(a_1, ... , a_i)$ 定义 $p'_j = \max\{x, a_1, ..., a_j\} $。我们发现此种情况下 $a$ 数列的个数等于 $dp_1(i-1,t-1) \cdot dp_2(n-i,t)$。
2. $\exists j < i$, $a_j = t$。我们枚举最小的 $j$,答案就是 $\sum_{j<i} dp_1(j-1,t-1) \cdot dp_2(n-j-1,t)$。(假想你有一个长 $n-1$ 的合法数列且 $t$ 首次出现位置小于 $i$,那么你在位置 $i$ 处插入一个 $t$ 就好了。)
我们注意到这两种情况需要的 $dp_1$ 和 $dp_2$ 都可以事先 $O(n^2)$ 预处理好,对 $\sum_{j<i} dp_1(j-1,t-1) \cdot dp_2(n-j-1,t)$ 处理出前缀和,就可以 $O(1)$ 计算出 $|\{a \in S_n | a_i = t\}|$ 了。
对于计算 $\sum_{1\le i<j \le n} |\{a \in S_n | a_i = a_j = t\}|$,我们对任意一个 $i$ 可以类似地 $O(1)$ 计算出 $\sum_{j: i<j \le n} |\{a \in S_n | a_i = a_j = t\}|$。因此对于一个 $t$,我们可以 $O(n)$ 的时间内算出 $\sum_{a \in S_n} cnt(a,t)^2$。因此总时间复杂度为 $O(n^2)$。
1 #include "bits/stdc++.h" 2 #define rep(i,a,n) for(int i=a;i<=n;i++) 3 #define per(i,a,n) for(int i=n;i>=a;i--) 4 #define pb push_back 5 #define mp make_pair 6 #define FI first 7 #define SE second 8 #define maxn 3000 9 #define inf 0x3f3f3f3f 10 using namespace std; 11 typedef long long ll; 12 typedef pair<int,int> pii; 13 typedef vector<int> vi; 14 typedef double db; 15 16 int mod; 17 18 int dp[maxn+5][maxn+5],dp2[maxn+5][maxn+5]; 19 int ans[maxn+5]; 20 21 int main() 22 { 23 int CAS; scanf("%d",&CAS); rep(cas,1,CAS) 24 { 25 int n; scanf("%d%d",&n,&mod); 26 dp[0][0]=1%mod; 27 rep(i,1,n) rep(x,1,i) dp[i][x]=(1ll*dp[i-1][x]*x+dp[i-1][x-1])%mod; 28 rep(x,0,n) dp2[0][x]=1%mod; 29 rep(i,0,n) dp2[i][n+1]=0; 30 rep(i,1,n) rep(x,1,n) dp2[i][x]=(1ll*dp2[i-1][x]*x+dp2[i-1][x+1])%mod; 31 32 rep(x,1,n) 33 { 34 int A=0,tmp=0,tmp2=0; 35 rep(i,x,n) 36 { 37 A=(A+1ll*dp[i-1][x-1]*dp2[n-i][x]+2ll*dp[i-1][x-1]*dp2[n-i-1][x]%mod*(n-i)+tmp+2ll*tmp2*(n-i))%mod; 38 if(i<=n-1) tmp=(tmp+1ll*dp[i-1][x-1]*dp2[n-i-1][x])%mod; 39 if(i<=n-2) tmp2=(tmp2+1ll*dp[i-1][x-1]*dp2[n-i-2][x])%mod; 40 } 41 ans[x]=A; 42 } 43 printf("Case #%d:\n",cas); 44 rep(i,1,n) printf("%d%c",ans[i]," \n"[i==n]); 45 } 46 return 0; 47 }
Problem I
Solution:
把技能看成向量。注意到两个向量 $a,b$ 张成的集合 $\{ra+sb|r,z \in \mathbb{Z}\} = \{r(a-b)+sb|r,z \in \mathbb{Z}\}$ ,于是我们可以对拥有的任意两个向量辗转相除,让其中一个向量的横坐标为 $0$。因此我们只需要维护两个向量 $p,q$ 其中 $p$ 横坐标非 $0$,$q$ 横坐标为 $0$。新获得一个向量就拿去先和 $p$ 辗转相除更新 $p$,余下的横坐标为 $0$ 的分量再拿去更新 $q$。查询的时候用 $p,q$ 很容易判断是否可行。
1 #include "bits/stdc++.h" 2 #define rep(i,a,n) for(int i=a;i<=n;i++) 3 #define per(i,a,n) for(int i=n;i>=a;i--) 4 #define pb push_back 5 #define mp make_pair 6 #define FI first 7 #define SE second 8 #define maxn 100000 9 #define mod 1000000007 10 #define inf 0x3f3f3f3f 11 using namespace std; 12 typedef long long ll; 13 typedef pair<int,int> pii; 14 typedef vector<int> vi; 15 typedef double db; 16 17 struct P 18 { 19 ll x,y; 20 P():x(0),y(0){} 21 P(ll x,ll y):x(x),y(y){} 22 P operator -(const P &a) const {return P(x-a.x,y-a.y);} 23 P operator *(const ll &a) const {return P(x*a,y*a);} 24 }; 25 26 void gcd(P &a,P &b) 27 { 28 while(a.x!=0) 29 { 30 ll t=b.x/a.x; 31 b=b-a*t; 32 swap(a,b); 33 } 34 } 35 36 int main() 37 { 38 int CAS; scanf("%d",&CAS); rep(cas,1,CAS) 39 { 40 P a,p; 41 int n; scanf("%d",&n); 42 ll ans=0; 43 while(n--) 44 { 45 int op; 46 ll x,y; scanf("%d%lld%lld",&op,&x,&y); 47 if(op==1) 48 { 49 P b(x,y); 50 gcd(b,a); 51 if(b.y) p.y=__gcd(p.y,abs(b.y)); 52 if(p.y) a.y%=p.y; 53 } 54 if(op==2) 55 { 56 P q(x,y); int w; scanf("%d",&w); 57 if(x) 58 { 59 if(a.x==0 || x%a.x!=0) continue; 60 ll t=x/a.x; 61 y-=t*a.y; 62 } 63 if(y==0) ans+=w; 64 else if(p.y && y%p.y==0) ans+=w; 65 } 66 } 67 printf("Case #%d: %lld\n",cas,ans); 68 } 69 return 0; 70 }
Problem J
Solution:
首先不同的长度 $d$ 排出来的矩阵肯定互不相同,因此我们可以对不同的 $d$ 单独处理。
先考虑 $d$ 整除 $n$ 的情况。此时只有一种切法,将串切成 $k=n/d$ 个小串。通过哈希合并相同的小串后,假设我们有 $k_1$ 个串 $s_1$, ..., $k_l$ 个串 $s_l$ ($k_1 + ... + k_l = k$),那么能排出的互不相同的矩阵一共有 $\frac{k!}{k_1!...k_l!}$ 个。
现在考虑 $d$ 不整除 $n$ 的情况。此时一共有 $\lfloor n/d \rfloor +1$ 种切法。注意到顺序/倒序枚举长度不够 $d$ 的那段小串的话,一种切法变到另一个切法对于上面所说的 $k_1, ..., k_l$ 的影响很小,只影响两个 $k_i$,因此可以 $O(1)$ 维护 $(k_1, ..., k_l)$。注意不同的切法有可能产生相同的 $(k_1, ..., k_l)$,所以这里还需要一共哈希来去重。
因此总时间复杂度为 $O(\sum_{d=1}^n n/d)$,即 $O(n\log n)$。注意别用 unoredered_map 存哈希,会被卡常。(手写或者用 pbds 里的 hash table 都可以过这题。)
1 #include "bits/stdc++.h" 2 #define rep(i,a,n) for(int i=a;i<=n;i++) 3 #define per(i,a,n) for(int i=n;i>=a;i--) 4 #define pb push_back 5 #define mp make_pair 6 #define FI first 7 #define SE second 8 #define maxn 400000 9 #define mod 998244353 10 #define inf 0x3f3f3f3f 11 using namespace std; 12 typedef long long ll; 13 typedef pair<int,int> pii; 14 typedef vector<int> vi; 15 typedef double db; 16 17 typedef unsigned long long hashv; 18 const hashv base=131,BASE=400009; 19 hashv pw[maxn+5],PW[maxn+5]; 20 21 char s[maxn+5]; 22 hashv hx[maxn+5]; 23 24 ll fac[maxn+5],ifac[maxn+5],inv[maxn+5]; 25 26 const int HASH=9999991; 27 28 struct HASHMAP 29 { 30 int head[HASH],next[maxn*3+5],tot; 31 hashv state[maxn*3+5]; 32 int f[maxn*3+5]; 33 vi used; 34 void init() 35 { 36 tot=0; 37 memset(head,-1,sizeof(head)); 38 } 39 void reset() 40 { 41 tot=0; 42 for(auto x: used) head[x]=-1; 43 used.clear(); 44 } 45 void insert(hashv val,int x) 46 { 47 int h=val%HASH; 48 used.pb(h); 49 for(int i=head[h];i!=-1;i=next[i]) if(val==state[i]) 50 { 51 f[i]=x; 52 return; 53 } 54 f[tot]=x; 55 state[tot]=val; 56 next[tot]=head[h]; 57 head[h]=tot++; 58 } 59 60 void add(hashv val,int x) 61 { 62 int h=val%HASH; 63 used.pb(h); 64 for(int i=head[h];i!=-1;i=next[i]) if(val==state[i]) 65 { 66 f[i]+=x; 67 return; 68 } 69 f[tot]=x; 70 state[tot]=val; 71 next[tot]=head[h]; 72 head[h]=tot++; 73 } 74 int ask(hashv val) 75 { 76 int h=val%HASH; 77 for(int i=head[h];i!=-1;i=next[i]) if(val==state[i]) return f[i]; 78 return 0; 79 } 80 int operator [](const hashv &x) {return ask(x);} 81 }H,id,M; 82 83 int main() 84 { 85 pw[0]=PW[0]=1; 86 rep(i,1,maxn) pw[i]=pw[i-1]*base; 87 rep(i,1,maxn) PW[i]=PW[i-1]*BASE; 88 89 inv[1]=fac[0]=ifac[0]=1; 90 rep(i,2,maxn) inv[i]=inv[mod%i]*(mod-mod/i)%mod; 91 rep(i,1,maxn) fac[i]=fac[i-1]*i%mod; 92 rep(i,1,maxn) ifac[i]=ifac[i-1]*inv[i]%mod; 93 94 H.init(); 95 id.init(); 96 M.init(); 97 98 int CAS; scanf("%d",&CAS); rep(cas,1,CAS) 99 { 100 scanf("%s",s+1); 101 int n=strlen(s+1); 102 rep(i,1,n) hx[i]=hx[i-1]*base+s[i]; 103 ll ans=0; 104 rep(d,1,n) 105 { 106 M.reset(); 107 id.reset(); 108 int cnt=0; 109 int N=n/d; 110 hashv I=0; 111 rep(j,1,N) 112 { 113 hashv h=hx[j*d]-hx[(j-1)*d]*pw[d]; 114 M.add(h,1); 115 if(id[h]==0) id.insert(h,++cnt); 116 I+=PW[id[h]]; 117 } 118 ll tmp=fac[N]; 119 rep(i,0,M.tot-1) tmp=tmp*ifac[M.f[i]]%mod; 120 ans=(ans+tmp)%mod; 121 int off=n%d; 122 if(n%d) 123 { 124 H.reset(); 125 H.insert(I,1); 126 per(j,1,N) 127 { 128 hashv oh=hx[j*d]-hx[(j-1)*d]*pw[d]; 129 tmp=tmp*fac[M[oh]]%mod; 130 M.add(oh,-1); 131 tmp=tmp*ifac[M[oh]]%mod; 132 hashv nh=hx[j*d+off]-hx[(j-1)*d+off]*pw[d]; 133 tmp=tmp*fac[M[nh]]%mod; 134 M.add(nh,1); 135 tmp=tmp*ifac[M[nh]]%mod; 136 if(id[nh]==0) id.insert(nh,++cnt); 137 I-=PW[id[oh]]; 138 I+=PW[id[nh]]; 139 if(H[I]==0) 140 { 141 ans=(ans+tmp)%mod; 142 H.insert(I,1); 143 } 144 } 145 } 146 } 147 printf("Case #%d: %lld\n",cas,ans); 148 } 149 return 0; 150 }
Problem K
Solution:
先假设对于每条从根到叶子的链我们都派一个人从根走到叶子然后停在叶子。那么对于一个叶子 $v$ 要花费 $dis(v)$ 的代价,其中 $dis(v)$ 表示根到点 $v$的距离。假设合并两条链 $root \rightarrow v, root \rightarrow v'$,让一个人先走到 $v$ 再走到 $v'$ 然后停下,会发现花费变为 $dis(v') + 2dis(v) -2dis(lca(u,v))$。这里观察到两个性质:一是让 $dis(v') \le dis(v)$ 在合并时会相对较好,二是如果我们对每个叶子 $v$ 定义 $mark(v)$ 为在其向上 $\lfloor dis(v) \rfloor$ 的祖先,那么我们发现合并 $v,v'$ 会变得更优当且仅当 $mark(v)$ 是 $mark(v')$ 的祖先。因此我们 DFS 的时候统计所有最深的 $mark(v)$ 带来的影响就行了。
1 #include "bits/stdc++.h" 2 #define rep(i,a,n) for(int i=a;i<=n;i++) 3 #define per(i,a,n) for(int i=n;i>=a;i--) 4 #define pb push_back 5 #define mp make_pair 6 #define FI first 7 #define SE second 8 #define maxn 1000000 9 #define mod 1000000007 10 #define inf 0x3f3f3f3f 11 using namespace std; 12 typedef long long ll; 13 typedef pair<int,int> pii; 14 typedef vector<int> vi; 15 typedef double db; 16 17 int val[maxn+5],sta[maxn+5]; 18 vi e[maxn+5]; 19 int ans=0; 20 21 int dfs(int now,int fa,int dep) 22 { 23 sta[dep]=now; 24 int x=sta[dep-(dep-1)/2]; 25 val[x]=max(val[x],dep-1); 26 int cnt=0; 27 for(auto v: e[now]) if(v!=fa) 28 { 29 cnt+=dfs(v,now,dep+1); 30 } 31 if(val[now]!=-1 && cnt==0) 32 { 33 cnt=1; 34 ans-=val[now]; 35 } 36 ans+=now==1?0:2*(max(1,cnt)); 37 return cnt; 38 } 39 40 int main() 41 { 42 int CAS; scanf("%d",&CAS); rep(cas,1,CAS) 43 { 44 int n; scanf("%d",&n); 45 rep(i,1,n) e[i].clear(),val[i]=-1; 46 rep(i,2,n) 47 { 48 int x; scanf("%d",&x); 49 e[i].pb(x); 50 e[x].pb(i); 51 } 52 ans=0; 53 dfs(1,0,1); 54 printf("Case #%d: %d\n",cas,ans); 55 } 56 return 0; 57 }
Problem L
Solution:
定义 $l_t[i] = \max\{l_{t-1}[i] + 1, l_{t-1}[i-1], l_{t-1}[i+1]\}$ 和 $r_t[i] = \min\{r_{t-1}[i] - 1, r_{t-1}[i-1], r_{t-1}[i+1]\}$,$l_0[i] = l[i]$,$r_0[i] = r[i]$。可以发现 $l_t[i] = \max_{i-t\ \le j \le i+t}{l[j] + (d - |j - i|)}$,$r_t[i] = \min_{i-t\ \le j \le i+t}{r[j] - (d - |j - i|)}$。我们发现如果 $r_t[i] < l_t[i]$,那么在时间 $t$ 时第 $i$ 列就会消失。因此我们可知 $ans[i] = \arg\min_{t\ge 1} \{r_t[i] < l_t[i]\}$。注意到 $ans[1]=1$,$|ans[i]-ans[i-1]| \le 1$,因此我们在确定 $ans[i-1]$ 后只需要枚举 $ans[i]$ 是 $\{ans[i-1]-1, ans[i-1], ans[i-1]+1\}$ 中的哪一个就行了。进一步发现这个可以用滑动窗口来做,时间复杂度为 $O(n)$。
1 #include "bits/stdc++.h" 2 #define rep(i,a,n) for(int i=a;i<=n;i++) 3 #define per(i,a,n) for(int i=n;i>=a;i--) 4 #define pb push_back 5 #define mp make_pair 6 #define FI first 7 #define SE second 8 #define maxn 5000000 9 #define mod 998244353 10 #define inf 0x3f3f3f3f 11 using namespace std; 12 typedef long long ll; 13 typedef pair<int,int> pii; 14 typedef vector<int> vi; 15 typedef double db; 16 17 typedef unsigned long long ull; 18 19 ull xorshift128p(ull &A,ull &B) 20 { 21 ull T=A,S=B; 22 A=S; 23 T^=T<<23; 24 T^=T>>17; 25 T^=S^(S>>26); 26 B=T; 27 return T+S; 28 } 29 30 void gen(int n,int L,int X,int Y,ull A,ull B,int l[],int r[]) 31 { 32 rep(i,1,n) 33 { 34 l[i]=xorshift128p(A,B)%L+X; 35 r[i]=xorshift128p(A,B)%L+Y; 36 if(l[i]>r[i]) swap(l[i],r[i]); 37 } 38 } 39 40 int l[maxn+5],r[maxn+5]; 41 int ans[maxn+5]; 42 43 struct monotone_queue 44 { 45 pii q[maxn+5]; 46 int front,rear; 47 void init() 48 { 49 front=0; rear=-1; 50 } 51 void push(int x,int id) 52 { 53 while(front<=rear && q[rear].FI>=x) rear--; 54 q[++rear]=mp(x,id); 55 } 56 void pop(int id) 57 { 58 while(front<=rear && q[front].SE<=id) front++; 59 } 60 int top() 61 { 62 if(front>rear) return inf; 63 return q[front].FI; 64 } 65 int ask(int id) 66 { 67 rep(i,front,rear) if(q[i].SE>=id) return q[i].FI; 68 return inf; 69 } 70 }q1,q2,q3,q4; 71 72 void solve(int n,int l[],int r[],int ans[]) 73 { 74 q1.init(); q2.init(); 75 q3.init(); q4.init(); 76 77 int d=1; 78 rep(i,1,n) 79 { 80 if(i==1) 81 { 82 q1.push(r[i+1]+i+1,i+1); 83 q3.push(r[i-1]-(i-1),i-1); 84 q3.push(r[i]-i,i); 85 q2.push(-(l[i+1]-(i+1)),i+1); 86 q4.push(-(l[i-1]+(i-1)),i-1); 87 q4.push(-(l[i]+i),i); 88 ans[i]=1; 89 } 90 else 91 { 92 q1.pop(i); q3.push(r[i]-i,i); 93 q2.pop(i); q4.push(-(l[i]+i),i); 94 d--; 95 rep(j,0,2) 96 { 97 if(min(q1.top()-d-i,q3.ask(i-d)-d+i)<max(-q2.top()+d+i,-q4.ask(i-d)+d-i)) 98 { 99 ans[i]=d; 100 q3.pop(i-d-1); q4.pop(i-d-1); 101 break; 102 } 103 else 104 { 105 d++; 106 q1.push(r[i+d]+i+d,i+d); 107 q2.push(-(l[i+d]-i-d),i+d); 108 } 109 } 110 } 111 } 112 } 113 114 int main() 115 { 116 int CAS; scanf("%d",&CAS); rep(cas,1,CAS) 117 { 118 int n,L,X,Y; ull A,B; scanf("%d%d%d%d%llu%llu",&n,&L,&X,&Y,&A,&B); 119 gen(n,L,X,Y,A,B,l,r); 120 l[0]=l[n+1]=inf,r[0]=r[n+1]=0; 121 solve(n,l,r,ans); 122 int ANS=0,pw=1; 123 rep(i,1,n) 124 { 125 ANS=(ANS+1ll*ans[i]*pw)%mod; 126 pw=3ll*pw%mod; 127 } 128 printf("Case #%d: %d\n",cas,ANS); 129 } 130 return 0; 131 }