2018.08.16
遗憾啊,三道题都不难,但是一道也没AC。
T1 mine
可以看出是个DP题(然而我考场上写的并不是DP,而且它对拍没错),
设:
f[i][j][0/1]代表已经处理完前i个数,且第i个数取j的方案数(0/1代表j==1时地雷在左/右,其他情况无用)
(j:3为是炸弹,0,1,2对应题目中的0,1,2)
于是便有:
f[i][0][0]=f[i-1][0][0]+f[i-1][1][0];(a[i]=='0'/'?')
f[i][1][0]=f[i-1][3][0];(a[i]=='1'/'?')
f[i][1][1]=f[i-1][0][0]+f[i-1][1][0];(a[i]=='1'/'?')
f[i][2][0]=f[i-1][3][0];(a[i]=='2'/'?')
f[i][3][0]=f[i-1][3][0]+f[i-1][1][1]+f[i-1][2][0];(a[i]=='3'/'?')(注意这里可以由上次的3转移)
设初值需要分类讨论,不再赘述。
1 #include<bits/stdc++.h> 2 #define int long long 3 #define AA puts("ALITA") 4 #define DD puts("DYBALA") 5 using namespace std; 6 const int N=1e6+10,mod=1e9+7; 7 int ans,n,f[N][4][2]; 8 char s[N]; 9 signed main() 10 { 11 //freopen("1.in","r",stdin); 12 //freopen("1.out","w",stdout); 13 scanf("%s",s+1); 14 n=strlen(s+1); 15 if(s[1]=='0'||s[1]=='?') f[1][0][0]=1; 16 if(s[1]=='1'||s[1]=='?') f[1][1][1]=1; 17 if(s[1]=='*'||s[1]=='?') f[1][3][0]=1; 18 for(int i=2;i<=n;i++) 19 { 20 if(s[i]=='0'||s[i]=='?') f[i][0][0]=(f[i-1][1][0]+f[i-1][0][0])%mod; 21 if(s[i]=='1'||s[i]=='?') 22 { 23 f[i][1][0]=f[i-1][3][0]; 24 f[i][1][1]=(f[i-1][0][0]+f[i-1][1][0])%mod; 25 } 26 if(s[i]=='2'||s[i]=='?') f[i][2][0]=f[i-1][3][0]; 27 if(s[i]=='*'||s[i]=='?') f[i][3][0]=(f[i-1][1][1]+f[i-1][2][0]+f[i-1][3][0])%mod; 28 } 29 if(s[n]=='0'||s[n]=='?') (ans+=f[n][0][0])%=mod; 30 if(s[n]=='1'||s[n]=='?') (ans+=f[n][1][0])%=mod; 31 if(s[n]=='*'||s[n]=='?') (ans+=f[n][3][0])%=mod; 32 printf("%lld",ans); 33 return 0; 34 }
T2 water
0~?分:
对于每个点枚举能到达的高度,bfs检查是否合法,时间复杂度$ O(n^2m^2*1e9) $
60分做法:
把枚举变为二分,复杂度大大降低,$ O(n^2m^2log2(1e9)) $,性价比较高
100分做法:
我们观察水流出的过程,可以看出一个块的ans一定为它走到矩阵外路径的最大值的最小值,
因为要想出去,一定是路径上最大值,而它可以从四面八方出去,所以在所以的路径的最大值中取min。
之后考虑如何建图,两个块之间的路的权值就是两个块的高度的最大值,源点的高度就是0。
1>求单源最短路,DJ即可(网格图Spfa可能会死)
2>最小生成树,f[i]即为i到源点(根)的路径的权值最大值。
1 #include<bits/stdc++.h> 2 #define int long long 3 #define max(a,b) (a>b?a:b) 4 #define AA puts("ALITA") 5 #define DD puts("DYBALA") 6 using namespace std; 7 const int N=305; 8 int sum,tot,cnt,T,n,m,a[N][N],ma[N][N],fa[N*N],head[N*N],to[N*N*2],ne[N*N*2],w[N*N*2],dis[N*N]; 9 int A[2]={0,-1}; 10 int B[2]={-1,0}; 11 struct edge{int fr,to,w;}e[N*N*4]; 12 int read() 13 { 14 register int sum,k=1;register char s; 15 while(s=getchar(),s<'0'||s>'9') if(s=='-') k=-1;sum=s-'0'; 16 while(s=getchar(),s>='0'&&s<='9') sum=sum*10+s-'0'; 17 return k*sum; 18 } 19 void add(int x,int y,int z) 20 { 21 e[++cnt].fr=x; 22 e[cnt].to=y; 23 e[cnt].w=z; 24 } 25 void connect(int x,int y,int z) 26 { 27 to[++sum]=y; 28 w[sum]=z; 29 ne[sum]=head[x]; 30 head[x]=sum; 31 } 32 bool comp(edge l,edge r) 33 { 34 return l.w<r.w; 35 } 36 int get(int x) 37 { 38 if(fa[x]==x) return x; 39 return fa[x]=get(fa[x]); 40 } 41 void dfs(int x,int father) 42 { 43 for(int i=head[x];i;i=ne[i]) 44 { 45 int y=to[i]; 46 if(y==father) continue; 47 dis[y]=max(dis[x],w[i]); 48 dfs(y,x); 49 } 50 } 51 signed main() 52 { 53 //freopen("1.in","r",stdin); 54 n=read();m=read(); 55 T=n*m+1; 56 for(int i=1;i<=n;i++) 57 { 58 for(int j=1;j<=m;j++) 59 { 60 ma[i][j]=++tot; 61 a[i][j]=read(); 62 } 63 } 64 for(int i=1;i<=n;i++) 65 { 66 for(int j=1;j<=m;j++) 67 { 68 for(int k=0;k<2;k++) 69 { 70 int l=i+A[k],r=j+B[k]; 71 if(!ma[l][r]) continue; 72 add(ma[i][j],ma[l][r],max(a[i][j],a[l][r])); 73 } 74 } 75 } 76 for(int i=1;i<=n;i++) 77 { 78 add(ma[i][1],T,max(a[i][1],0)); 79 add(ma[i][m],T,max(a[i][m],0)); 80 } 81 for(int i=2;i<m;i++) 82 { 83 add(ma[1][i],T,max(a[1][i],0)); 84 add(ma[n][i],T,max(a[n][i],0)); 85 } 86 for(int i=1;i<=tot+1;i++) fa[i]=i; 87 sort(e+1,e+cnt+1,comp); 88 for(int i=1,x,y;i<=cnt;i++) 89 { 90 x=get(e[i].fr),y=get(e[i].to); 91 if(x==y) continue; 92 fa[x]=y; 93 connect(e[i].fr,e[i].to,e[i].w); 94 connect(e[i].to,e[i].fr,e[i].w); 95 } 96 dfs(T,0); 97 for(int i=1;i<=n;i++) 98 { 99 for(int j=1;j<=m;j++) 100 { 101 printf("%lld ",dis[ma[i][j]]-a[i][j]); 102 } 103 puts(""); 104 } 105 return 0; 106 }
T3 gcd
考场上想到了正解,但当时一时脑抽觉得一个500000的数的质因子可能有好多好多个...(大雾)
20分做法:
暴力搞它~,时间复杂度$ O(nm*2log2(max(a,b)) $
50分做法:
30分部分分满足max(a[i])<=100,于是可以用一个桶来存储,更新答案,复杂度$ O(m*100)
(注意gcd要预处理,否则像我这样自带大常数的根本过不去!!!)
100分做法:
跟题解不太一样,但又有异曲同工之妙。
设sum1为原来被激活的数的个数,sum2为其中与a[i]组成不合法的数的个数
显然nowans=lastans+sum1-sum2;sum1好求,关键在于sum2。
仍然是用桶v[x]代表x|a[i]的个数,我们可以容斥来解决,
具体来说就是:枚举每个素因子是否取,在$ O(2^6) $内求出每种情况,容斥即可。
1 #include<bits/stdc++.h> 2 #define int long long 3 #define max(a,b) (a>b?a:b) 4 #define AA puts("ALITA") 5 #define DD puts("DYBALA") 6 using namespace std; 7 const int N=2e5+10; 8 int sum,ans,n,m,tot[N],mark[N],a[N],p[N][8],v[N*3]; 9 int read() 10 { 11 register int sum,k=1;register char s; 12 while(s=getchar(),s<'0'||s>'9') if(s=='-') k=-1;sum=s-'0'; 13 while(s=getchar(),s>='0'&&s<='9') sum=sum*10+s-'0'; 14 return k*sum; 15 } 16 void init(int x,int y) 17 { 18 for(int i=2;i<=sqrt(x);i++) 19 { 20 if(x%i) continue; 21 p[y][++tot[y]]=i; 22 while(x%i==0) x/=i; 23 } 24 if(x!=1) p[y][++tot[y]]=x; 25 } 26 void dfs(int x,int y,int z,int val) 27 { 28 if(y==tot[x]+1) 29 { 30 if(val==1) return; 31 ans+=v[val]*z; 32 return; 33 } 34 dfs(x,y+1,z*(-1),val*p[x][y]); 35 dfs(x,y+1,z,val); 36 } 37 void redfs(int x,int y,int z,int val) 38 { 39 if(y==tot[x]+1) 40 { 41 v[val]+=z; 42 return; 43 } 44 redfs(x,y+1,z,val*p[x][y]); 45 redfs(x,y+1,z,val); 46 } 47 signed main() 48 { 49 //freopen("1.in","r",stdin); 50 n=read();m=read(); 51 for(int i=1;i<=n;i++) 52 { 53 a[i]=read(); 54 init(a[i],i); 55 } 56 for(int i=1,x;i<=m;i++) 57 { 58 x=read(); 59 mark[x]^=1; 60 if(mark[x]) 61 { 62 ans+=sum; 63 sum++; 64 dfs(x,1,1,1); 65 redfs(x,1,1,1); 66 } 67 else 68 { 69 sum--; 70 ans-=sum; 71 redfs(x,1,-1,1); 72 dfs(x,1,-1,1); 73 } 74 printf("%lld\n",ans); 75 } 76 return 0; 77 }