zoukankan      html  css  js  c++  java
  • 2020 CCPC Qinhuangdao Solutions

    Problem A

    Solution:

      很简单的组合计数公式题,答案是:$frac{inom{r}{2}}{inom{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
    ",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:
    ",cas);
    122         rep(q,1,Q) if(OP[q].FI==2) printf("%d
    ",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
    ",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
    ",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
    ",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} floor = i, xle n, x equiv 0 ( ext{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
    ",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_{1le i,j le n} |{a in S_n | a_i = a_j = t}| = sum_{1le ile n} |{a in S_n | a_i = t}| + 2 sum_{1le 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_{1le 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:
    ",cas);
    44         rep(i,1,n) printf("%d%c",ans[i]," 
    "[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
    ",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 floor +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(nlog 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
    ",cas,ans);
    148     }
    149     return 0;
    150 }
    View Code

    Problem K

    Solution:

      先假设对于每条从根到叶子的链我们都派一个人从根走到叶子然后停在叶子。那么对于一个叶子 $v$ 要花费 $dis(v)$ 的代价,其中 $dis(v)$ 表示根到点 $v$的距离。假设合并两条链 $root ightarrow v, root ightarrow v'$,让一个人先走到 $v$ 再走到 $v'$ 然后停下,会发现花费变为 $dis(v') + 2dis(v) -2dis(lca(u,v))$。这里观察到两个性质:一是让 $dis(v') le dis(v)$ 在合并时会相对较好,二是如果我们对每个叶子 $v$ 定义 $mark(v)$ 为在其向上 $lfloor dis(v) floor$ 的祖先,那么我们发现合并 $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
    ",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] = argmin_{tge 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
    ",cas,ANS);
    129     }
    130     return 0;
    131 }
    View Code
  • 相关阅读:
    数组 滑动窗口
    爬虫案例 下载某文库付费文档 全格式
    双指针 三数之和
    双指针 四数之和
    双指针法 环形链表 II
    判断是否手机端
    C# 模拟点击
    chrome 扩展开发注意事项
    破解拖动验 证码
    //刷新任务栏图标 终止别的进程序有些程序有托盘会残留
  • 原文地址:https://www.cnblogs.com/YaoBIG/p/13848448.html
Copyright © 2011-2022 走看看