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 }
View Code

 

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 }
View Code

 

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 }
View Code

 

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 }
View Code

 

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 }
View Code

 

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 }
View Code

 

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 }
View Code

 

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 }
View Code

 

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 }
View Code

 

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 }
View Code

 

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 }
View Code

 

posted @ 2020-10-20 20:17  YaoBIG  阅读(919)  评论(3编辑  收藏  举报