• 计蒜之道2019复赛题解


    A.外教 Michale 变身大熊猫

    对每个i求出以它结尾的[1,i]中的LIS长度f1与个数g1,和以它开头的[i,n]中的LIS长度f2与个数g2,若f1+f2-1=整个数列的LIS长度,那么它出现在LIS中的概率就是g1*g2/整个数列LIS的个数。发现可以用线段树优化朴素DP转移,维护下区间最大值和最大值的个数即可快速求出f和g。

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 #include<iostream>
     5 #define ls (x<<1)
     6 #define rs (ls|1)
     7 #define lson ls,L,mid
     8 #define rson rs,mid+1,R
     9 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
    10 typedef long long ll;
    11 using namespace std;
    12 
    13 const int N=1000010,mod=998244353;
    14 int n,tot,res,sm,a[N],b[N],f1[N],f2[N],g1[N],g2[N],v1[N<<2],v2[N<<2];
    15 
    16 int ksm(int a,int b){
    17     int res=1;
    18     for (; b; a=1ll*a*a%mod,b>>=1)
    19         if (b & 1) res=1ll*res*a%mod;
    20     return res;
    21 }
    22 
    23 void upd(int x,int y,int &r1,int &r2){
    24     if (x>r1) r1=x,r2=y; else if (x==r1) r2=(r2+y)%mod;
    25 }
    26 
    27 void build(int x,int L,int R){
    28     v1[x]=v2[x]=0;
    29     if (L==R) return;
    30     int mid=(L+R)>>1; build(lson); build(rson);
    31 }
    32 
    33 void mdf(int x,int L,int R,int p,int k1,int k2){
    34     if (L==R){ upd(k1,k2,v1[x],v2[x]); return; }
    35     upd(k1,k2,v1[x],v2[x]);
    36     int mid=(L+R)>>1;
    37     if (p<=mid) mdf(lson,p,k1,k2); else mdf(rson,p,k1,k2);
    38 }
    39 
    40 void que(int x,int L,int R,int l,int r,int &r1,int &r2){
    41     if (L==l && r==R){ upd(v1[x],v2[x],r1,r2); return; }
    42     int mid=(L+R)>>1;
    43     if (r<=mid) que(lson,l,r,r1,r2);
    44     else if (l>mid) que(rson,l,r,r1,r2);
    45         else que(lson,l,mid,r1,r2),que(rson,mid+1,r,r1,r2);
    46 }
    47 
    48 int main(){
    49     freopen("a.in","r",stdin);
    50     freopen("a.out","w",stdout);
    51     scanf("%d",&n);
    52     rep(i,1,n) scanf("%d",&a[i]),tot=max(tot,a[i]),b[i]=a[i];
    53     sort(b+1,b+n+1); tot=unique(b+1,b+n+1)-b-1;
    54     rep(i,1,n) a[i]=lower_bound(b+1,b+tot+1,a[i])-b;
    55     build(1,0,tot+1); mdf(1,0,tot+1,0,0,1);
    56     rep(i,1,n) que(1,0,tot+1,0,a[i]-1,f1[i],g1[i]),f1[i]++,mdf(1,0,tot+1,a[i],f1[i],g1[i]);
    57     build(1,0,tot+1); mdf(1,0,tot+1,tot+1,0,1);
    58     for (int i=n; i; i--) que(1,0,tot+1,a[i]+1,tot+1,f2[i],g2[i]),f2[i]++,mdf(1,0,tot+1,a[i],f2[i],g2[i]);
    59     que(1,0,tot+1,0,tot+1,res,sm); sm=ksm(sm,mod-2);
    60     rep(i,1,n) if (f1[i]+f2[i]-1==res) printf("%lld ",1ll*g1[i]*g2[i]%mod*sm%mod); else printf("0 ");
    61     return 0;
    62 }
    View Code

    B.个性化评测系统

    先枚举听的牌,再枚举对子,然后就只剩判断剩下的能不能组成四副刻子了。从小到大枚举牌型,能面子就面子,剩下的只能组成顺子,很好判和。

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 #include<iostream>
     5 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
     6 typedef long long ll;
     7 using namespace std;
     8 
     9 const int N=110;
    10 char s[N];
    11 int d[N],p[N];
    12 
    13 void work(char s[]){
    14     if (s[1]=='m') d[s[0]-'0']++;
    15     if (s[1]=='s') d[s[0]-'0'+9]++;
    16     if (s[1]=='p') d[s[0]-'0'+18]++;
    17     if (s[1]=='z') d[s[0]-'0'+27]++;
    18 }
    19 
    20 void work2(int x){
    21     if (x<=9) printf("%dm
    ",x);
    22     if (x>9 && x<=18) printf("%ds
    ",x-9);
    23     if (x>18 && x<=27) printf("%dp
    ",x-18);
    24     if (x>27) printf("%dz
    ",x-27);
    25 }
    26 
    27 bool chk(){
    28     rep(x,1,34) if (d[x]>=2){
    29         rep(i,1,34) p[i]=d[i];
    30         p[x]-=2; bool flag=0;
    31         rep(i,1,34) if (p[i]){
    32             if (p[i]>=3) p[i]-=3;
    33             if (!p[i]) continue;
    34             if (i==8 || i==9 || i==17 || i==18 || i==26 || i==27 || i==33 || i==34 || p[i+1]<p[i] || p[i+2]<p[i]){ flag=1; break; }
    35             p[i+1]-=p[i]; p[i+2]-=p[i]; p[i]=0;
    36         }
    37         if (!flag) return 1;
    38     }
    39     return 0;
    40 }
    41 
    42 int main(){
    43     freopen("b.in","r",stdin);
    44     freopen("b.out","w",stdout);
    45     while (~scanf("%s",s)){
    46         rep(i,0,34) d[i]=0; work(s);
    47         rep(i,2,13) scanf("%s",s),work(s);
    48         rep(i,1,34) if (d[i]<=3) { d[i]++; if (chk()) work2(i); d[i]--; }
    49     }
    50     return 0;
    51 }
    View Code

    C.个性化学习之石子游戏

    把SG表打出来发现就是末尾0的个数,然后问题就是想求有多少种方案能让整个数列异或和为0。先高精度对每个i统计出末尾有i个0的数有多少个,再FWT即可。注意这题似乎卡时,若高精度除法的时候没有余数(即被除数是偶数)则可以直接O(1)统计,不需要重新计算了。

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
     5 typedef long long ll;
     6 using namespace std;
     7 
     8 const int N=1000010,mod=998244353,i2=499122177;
     9 char s[N];
    10 ll m;
    11 int n,tot,a[N],c[N];
    12 
    13 int ksm(int a,int b){
    14     int res=1;
    15     for (; b; a=1ll*a*a%mod,b>>=1)
    16         if (b & 1) res=1ll*res*a%mod;
    17     return res;
    18 }
    19 
    20 void FWT(int a[],int n,int f){
    21     for (int i=1; i<n; i<<=1)
    22         for (int p=i<<1,j=0; j<n; j+=p)
    23             for (int k=0; k<i; k++){
    24                 int x=a[j+k],y=a[i+j+k];
    25                 if (f) a[j+k]=(x+y)%mod,a[i+j+k]=(x-y+mod)%mod;
    26                     else a[j+k]=1ll*(x+y)*i2%mod,a[i+j+k]=1ll*(x-y+mod)*i2%mod;
    27             }
    28 }
    29 
    30 int main(){
    31     freopen("c.in","r",stdin);
    32     freopen("c.out","w",stdout);
    33     scanf("%s%lld",s+1,&m); n=strlen(s+1); m%=mod-1; tot=-1;
    34     rep(i,1,n) a[i]=s[n-i+1]-'0';
    35     c[++tot]=a[n];
    36     for (int i=n-1; i; i--) c[tot]=(c[tot]*10ll+a[i])%mod;
    37     int p=ksm(c[tot],m);
    38     while (n){
    39         for (int i=n; i; i--) a[i-1]+=(a[i]&1)*10,a[i]>>=1;
    40         if (!a[0]) tot++,c[tot]=1ll*c[tot-1]*i2%mod;
    41         else{
    42             c[++tot]=a[n];
    43             for (int i=n-1; i; i--) c[tot]=(c[tot]*10ll+a[i])%mod;
    44         }
    45         a[0]=0; while (n && !a[n]) n--;
    46     }
    47     rep(i,0,tot-1) c[i]=(c[i]-c[i+1]+mod)%mod;
    48     for (n=1; n<=tot; n<<=1);
    49     FWT(c,n,1);
    50     rep(i,0,n-1) c[i]=ksm(c[i],m)%mod;
    51     FWT(c,n,0); printf("%d
    ",(p-c[0]+mod)%mod);
    52     return 0;
    53 }
    View Code

    D.“星云系统”

    先写了一个序列自动机,MLE。

    再写了一个枚举+二分,TLE。

    考虑线性做法,从前往后枚举,当后面一个字典序更小的字符可以出现在答案中时,可以考虑用它替代前面的字符,栈维护。

     1 #include<vector>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<algorithm>
     5 #include<iostream>
     6 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
     7 typedef long long ll;
     8 using namespace std;
     9 
    10 const int N=5000010;
    11 char s[N],q[N];
    12 int n,K,top;
    13 
    14 int main(){
    15     freopen("d.in","r",stdin);
    16     freopen("d.out","w",stdout);
    17     scanf("%s%d",s+1,&K); n=strlen(s+1);
    18     rep(i,1,n){
    19         while (top && n-i+1+top>K && q[top]>s[i]) top--;
    20         q[++top]=s[i];
    21     }
    22     rep(i,1,K) putchar(q[i]);
    23     return 0;
    24 }
    View Code

    E.撑起信息安全“保护伞”

    前驱:找到最靠后的一个可以被替代为'('的')',这个位置之前的不动,这个位置替代为')',之后重新构造。后继同理。

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 #include<iostream>
     5 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
     6 typedef long long ll;
     7 using namespace std;
     8 
     9 const int N=2000010;
    10 char s[N];
    11 int n,sm[N];
    12 
    13 int main(){
    14     freopen("e.in","r",stdin);
    15     freopen("e.out","w",stdout);
    16     scanf("%s",s+1); n=strlen(s+1);
    17     rep(i,1,n) if (s[i]=='(') sm[i]=sm[i-1]+1; else sm[i]=sm[i-1]-1;
    18     for (int i=n; i; i--) if (s[i]==')' && ((sm[i-1]+1)&1)==((n-i)&1) && sm[i-1]+1<=n-i){
    19         rep(j,1,i-1) putchar(s[j]); putchar('('); int t=sm[i-1]+1;
    20         rep(j,i+1,n) if (t) putchar(')'),t--; else putchar('('),t++;
    21         break;
    22     }
    23     puts("");
    24     for (int i=n; i; i--) if (s[i]=='(' && ((sm[i-1]-1)&1)==((n-i)&1) && sm[i-1]){
    25         rep(j,1,i-1) putchar(s[j]); putchar(')'); int t=sm[i-1]-1;
    26         rep(j,i+1,n) if (t+1<=n-j) putchar('('),t++; else putchar(')'),t--;
    27         break;
    28     }
    29     return 0;
    30 }
    View Code
  • 相关阅读:
    彻悟大师语录
    读书
    复变函数简要
    【洛谷P4781】【模板】拉格朗日插值
    【洛谷P4585】火星商店问题
    【YbtOJ#593】木棍问题
    【YbtOJ#893】带权的图
    【洛谷P4735】最大异或和
    【洛谷P5787】二分图 /【模板】线段树分治
    【ARC098D】Donation
  • 原文地址:https://www.cnblogs.com/HocRiser/p/11037847.html
Copyright © 2020-2023  润新知