8.16题解
T1
废物除了搜索啥都不会打,连个垃圾DP都推不出来,事实上知道他是DP之后,依旧打了很久,我的DP状态和大家都不太一样,比较清奇,不过我觉得很好想,$dp[i][j][k]$表示第$i$位上为$k$前一位上为$j$的方案数,$j$和$k$均在$0\thicksim3$的范围内,$0$ $1$ $2$参照题面,$3$代表此处有雷
思考
先来思考一下在大多数情况下那些方案对于相邻两位是合法的
00 两位上均没有雷且相邻位置上也没有雷 01 两位上均没有雷,后一个位置的相邻位置上有一个位置有雷,显然是右边 10 两位上均没有雷,前一个位置的相邻位置上有一个位置有雷,显然是左边 11 两位上均没有雷,且两个位置的相邻位置上都有一个位置有雷,显然左边一个右边一个 13 前一位上没雷,后一位上有雷 23 前一位左右两侧都有雷,后一位肯定是个雷 前一位是个雷,一共三种情况 31 32 33
初始化
由于$dp$定义的特殊性,我的初始化非常麻烦,直接初始化$dp[2]$,判断一下第一二位可能出现的情况,后面就直接从$dp[3]$开始转移即可
如果第一二位上均为$?$,那么我们只需要排除上面提到的9种情况中不合法的
10 上面有提到他需要前一位的左边有雷,但是第一位已经是最左,所以不合法 11 同上,第一位左侧不可能有雷 23 同上,左侧无雷
如果只有第一位是$?$,第二位已知直接就按照第二位,判断第一位可能情况即可
else if(a[1]==4)//4代表该处为? { //当前为0,在上述的可能情况中选择,第一位只能为0 if(a[2]==0) dp[2][0][0]=1ll*1; //两种情况均可,对于前一种,第三位肯定是个3,当然,这是后话 if(a[2]==1) {dp[2][0][1]=1ll*1; dp[2][3][1]=1ll*1;} //第二位为2,第一位肯定是个雷 if(a[2]==2) dp[2][3][2]=1ll*1; //第二位有个雷,第一位要么是个雷,要么不是雷那他的相邻位置上就必须标记有雷 //注意23状态对于前两位,已经被判不合法 if(a[2]==3) {dp[2][1][3]=1ll*1; dp[2][3][3]=1ll*1;} }
同理判断只有第二位为$?$的情况,可以自己思考一下
else if(a[2]==4) { if(a[1]==0) {dp[2][0][0]=1ll*1; dp[2][0][1]=1ll*1;} if(a[1]==1) dp[2][1][3]=1ll*1; if(a[1]==3) {dp[2][3][1]=1ll*1; dp[2][3][2]=1ll*1; dp[2][3][3]=1ll*1;} }
两位都不为$?$要么不合法,要么就直接用,由于数据里没有不合法,所以我没判,严谨一点的话是需要判的
状态转移
当然又是一个长篇大论的分情况讨论,我们用$dp[i-1]$更新$dp[i]$
假设第$i$个位置上是0,由最初状态,状态只能是$00$或$10$,考虑这两种状态分别由哪些状态转移得来的,可以在列出的状态中选择合法状态,$00$可以由$00$和$10$转移得来,注意$dp[i]$的第二维和$dp[i-1]$的第三维之间的对应关系
第$i$个位置上为1,合法状态有$01$ $11$ $31$,$01$可以由$10$和$00$转移来,$11$由$31$转移来,$31$由$13$ $23$ $33$转移来
第$i$个位置为2或3时的状态转移可以自己想一想,想过之后去我的标程里对照一下,为5的时候就把前面的情况全和起来就可以了,我才不承认我是因为懒才没写全的
对于这种大量分情况讨论的题,转移的过程中一定要注意方案的合法性,保证补充不漏
统计答案
排除对最后一位来说的不合法状态即可
01 右侧不可能再出现雷 11 同上,右侧无雷 32 同上,右侧无雷
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #define maxn 1001000 5 #define ll long long 6 #define mod 1000000007 7 using namespace std; 8 int n; 9 ll ans; 10 int a[maxn]; 11 char s[maxn]; 12 ll dp[maxn][5][5]; 13 int main() 14 { 15 scanf("%s",s); n=strlen(s); 16 for(int i=1;i<=n;++i) 17 { 18 if(s[i-1]=='0') a[i]=0; 19 else if(s[i-1]=='1') a[i]=1; 20 else if(s[i-1]=='2') a[i]=2; 21 else if(s[i-1]=='*') a[i]=3; 22 else a[i]=4; 23 } 24 if(a[1]==4&&a[2]==4) 25 { 26 dp[2][0][0]=1ll*1; dp[2][0][1]=1ll*1; dp[2][1][3]=1ll*1; 27 dp[2][3][1]=1ll*1; dp[2][3][2]=1ll*1; dp[2][3][3]=1ll*1; 28 } 29 else if(a[1]==4) 30 { 31 if(a[2]==0) dp[2][0][0]=1ll*1; 32 if(a[2]==1) {dp[2][0][1]=1ll*1; dp[2][3][1]=1ll*1;} 33 if(a[2]==2) dp[2][3][2]=1ll*1; 34 if(a[2]==3) {dp[2][1][3]=1ll*1; dp[2][3][3]=1ll*1;} 35 } 36 else if(a[2]==4) 37 { 38 if(a[1]==0) {dp[2][0][0]=1ll*1; dp[2][0][1]=1ll*1;} 39 if(a[1]==1) dp[2][1][3]=1ll*1; 40 if(a[1]==3) {dp[2][3][1]=1ll*1; dp[2][3][2]=1ll*1; dp[2][3][3]=1ll*1;} 41 } 42 else dp[2][a[1]][a[2]]=1ll*1; 43 for(int i=3;i<=n;++i) 44 { 45 if(a[i]==0) 46 { 47 dp[i][0][0]=(dp[i-1][1][0]+dp[i-1][0][0])%mod; 48 dp[i][1][0]=dp[i-1][3][1]; 49 } 50 else if(a[i]==1) 51 { 52 dp[i][0][1]=(dp[i-1][1][0]+dp[i-1][0][0])%mod; 53 dp[i][1][1]=dp[i-1][3][1]; 54 dp[i][3][1]=((dp[i-1][1][3]+dp[i-1][2][3])%mod+dp[i-1][3][3])%mod; 55 } 56 else if(a[i]==2) 57 dp[i][3][2]=((dp[i-1][1][3]+dp[i-1][2][3])%mod+dp[i-1][3][3])%mod; 58 else if(a[i]==3) 59 { 60 dp[i][1][3]=(dp[i-1][0][1]+dp[i-1][1][1])%mod; 61 dp[i][2][3]=dp[i-1][3][2]; 62 dp[i][3][3]=((dp[i-1][1][3]+dp[i-1][2][3])%mod+dp[i-1][3][3])%mod; 63 } 64 else 65 { 66 dp[i][0][0]=(dp[i-1][1][0]+dp[i-1][0][0])%mod; 67 dp[i][1][0]=dp[i-1][3][1]; 68 dp[i][0][1]=(dp[i-1][1][0]+dp[i-1][0][0])%mod; 69 dp[i][1][1]=dp[i-1][3][1]; 70 dp[i][3][1]=((dp[i-1][1][3]+dp[i-1][2][3])%mod+dp[i-1][3][3])%mod; 71 dp[i][3][2]=((dp[i-1][1][3]+dp[i-1][2][3])%mod+dp[i-1][3][3])%mod; 72 dp[i][1][3]=(dp[i-1][0][1]+dp[i-1][1][1])%mod; 73 dp[i][2][3]=dp[i-1][3][2]; 74 dp[i][3][3]=((dp[i-1][1][3]+dp[i-1][2][3])%mod+dp[i-1][3][3])%mod; 75 } 76 } 77 ans=((ans+dp[n][0][0])%mod+dp[n][1][0])%mod; 78 ans=((ans+dp[n][3][1])%mod+dp[n][1][3])%mod; 79 ans=((ans+dp[n][2][3])%mod+dp[n][3][3])%mod; 80 printf("%lld\n",ans); 81 return 0; 82 }
T2
考试结束前三分钟,觉得可以和图论扯在一起,然而晚了
对于一个格子里水,他一定是沿着一条最矮的路流出去的,那么我们可以给相邻两点之间建边,以两点中的较大高度为边权,给最外面一圈点和外界连边,然后跑最小生成树,这样跑出来的就是最矮的一条路,也就是使所有点流出去的一条路,然后再$dfs$一遍,找到每个点流出去的路径上边权最大的边更新当前点的答案,因为一定是路径上最高的点限制了水流出去
1 #include<algorithm> 2 #include<iostream> 3 #include<cstdio> 4 #define maxn 310 5 #define bh(i,j) ((i-1)*m+j) 6 using namespace std; 7 struct node{ 8 int from,to,w; 9 }a[maxn*maxn*4]; 10 int n,m,tot,js; 11 int fa[maxn*maxn]; 12 int head[maxn*maxn],to[maxn*maxn*8],xia[maxn*maxn*8],w[maxn*maxn*8]; 13 int pd[maxn*maxn],ans[maxn*maxn]; 14 int b[maxn][maxn]; 15 int find(int x) 16 { 17 if(fa[x]!=x) return fa[x]=find(fa[x]); 18 return fa[x]; 19 } 20 void add(int x,int y,int z) 21 { 22 to[++js]=y; xia[js]=head[x]; w[js]=z; head[x]=js; 23 } 24 bool cmp(const node &a,const node &b) 25 { 26 return a.w<b.w; 27 } 28 void me(int x,int y) 29 { 30 x=find(x); y=find(y); 31 fa[y]=x; 32 } 33 void dfs(int x) 34 { 35 pd[x]=1; 36 for(int i=head[x];i;i=xia[i]) 37 { 38 int ls=to[i]; 39 if(!pd[ls]) {ans[ls]=max(ans[x],w[i]); dfs(ls);} 40 } 41 } 42 int main() 43 { 44 // freopen("2.in","r",stdin); 45 // freopen("WA.out","w",stdout); 46 scanf("%d%d",&n,&m); 47 for(int i=1;i<=n;++i) 48 { 49 for(int j=1;j<=m;++j) 50 { 51 scanf("%d",&b[i][j]); 52 if(i==1||i==n||j==1||j==m) 53 {a[++tot].from=0; a[tot].to=bh(i,j); a[tot].w=max(0,b[i][j]);} 54 if(i-1>=1) 55 { 56 a[++tot].from=bh(i-1,j); a[tot].to=bh(i,j); 57 a[tot].w=max(b[i][j],b[i-1][j]); 58 } 59 if(j-1>=1) 60 { 61 a[++tot].from=bh(i,j-1); a[tot].to=bh(i,j); 62 a[tot].w=max(b[i][j],b[i][j-1]); 63 } 64 } 65 } 66 for(int i=0;i<=n*m;++i) fa[i]=i; 67 int tt=0; 68 sort(a+1,a+tot+1,cmp); 69 for(int i=1;i<=tot;++i) 70 { 71 int fo=a[i].from,t=a[i].to; 72 if(find(fo)!=find(t)) 73 { 74 me(fo,t); add(fo,t,a[i].w); add(t,fo,a[i].w); 75 tt++; 76 if(tt==n*m) break; 77 } 78 } 79 for(int i=0;i<=n*m+10;++i) ans[i]=-1000000100; 80 dfs(0); 81 for(int i=1;i<=n;++i) 82 { 83 for(int j=1;j<=m;++j) printf("%d ",max(0,ans[bh(i,j)]-b[i][j])); 84 puts(""); 85 } 86 return 0; 87 }
T3
莫比乌斯反演,咕了