计蒜之道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\n",x);
22     if (x>9 && x<=18) printf("%ds\n",x-9);
23     if (x>18 && x<=27) printf("%dp\n",x-18);
24     if (x>27) printf("%dz\n",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\n",(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

 

posted @ 2019-06-17 09:06  HocRiser  阅读(263)  评论(0编辑  收藏  举报