Description
菁菁堂有一块数字格,那是王解体最喜欢去的地方。
传说中,这条气势磅礴的数字格,有N行N列,每一个格子里均有一个数。
敢于挑战自己的王解体决定来挑战这道通过率为百分之九十九的题目。
格子的第一行及第一列均是给定的:
F[k,1]=lk
F[1,k]=tk
对于其他格子,满足递推式:
F[i,j]=aF[i,j-1]+bF[i-1,j]+c
不出所料,当王解体能得到 F[n,n](mod 1000003)时,通过率将达到百分之百。
Input
题目包含多组测试数据,每组测试数据占3行。
对于每组测试数据:
第一行包含4个整数N,a,b,c。
第二行包含N个整数,表示l[1],…,l[N]
第三行包含N个整数,表示t[1],…,t[N]
Output
对于每组数据,输出一行,包含一个整数,表示王解体得到的答案。
Sample Input
3 0 0 0
0 0 2
0 3 0
4 3 5 2
7 1 4 3
7 4 4 8
Sample Output
0
41817
Data Constraint
10%的数据:N<=1000
30%的数据:N<=10000
100%的数据:N<=200000,a,b,c<=1000000,l[1]=t[1],1<=l[k],t[k]<=1000000
思路
我们可以根据棋盘上的箭头信息建一个有向图,其中图中每个节点的出度最多为 1,所
以我们会得到一个有以下两种部分构成的图:
- 有向树
- 环加多个有向树
每一条从第一列的节点到最后一列的节点的路径对应了一条从一个树上的节点到对应
树根的路径。我们可以将第一列的节点看成是特殊节点,剩下的问题就变成了如何对树上的
一些点进行染色,使得从这些特殊点到根的路径上包含有且只有一个有色点。
在实现的过程中,为了实现的方便,我们可以多加一个辅助根,所有的根都向辅助根连
一条有向边。这样就能将多棵树上的问题转化为一棵树上的问题。
剩下的问题可以用动态规划解决。用 f(x,i,k)表示我们对 x 的前 i 个子树的 k 个点进行染
色,是否能使得子树中每个特殊点到节点 x 的路径都有且只有一个有色点。假设我们在第 i
个子树染了 k 个节点,如果 f(x,i+1,k-y)和 f(child[x][i],0,y)的值都为 1,那么 f(x,i,k)的值也可以
为 1。如果有环的话,可以想象环上一定没有最后一列点,所以环上的点可以任意染色。这
样一来,总的时间复杂度是 O(NMK^2)的。
为了加速计算过程,我们可以用二进制串对状态进行压缩,将所有 f(x,i+1,k)压缩成一个
k 位的二进制串表示成 F[x][i]。另外我们可以用 R[x][i]来表示 F[x][i]翻转后的值。我们可以推
导出 F[x][i]=(R[x][i] >> (50-k)) & F[child[x][i]]。这么一来,优化后的时间复杂度为 O(NMK)。
代码
#include<bits/stdc++.h>
using namespace std;
const int N=1000077,K=51;
int n,m,i,j,k,tot,ans,s,t,x,y,z,r,c,last,ss;
int w[5],go[N],g[N][K];
int q[N][2],nex[N],head[N],dfs[N],dfn[N],size[N];
bool f[N][K];
char ch;
bool bz[N],b[N];
bool dg(int i,int fa) {
bool re=false;
dfn[i]=++t; size[dfn[i]]=1;
int last;
dfs[t]=i;
if (bz[dfn[fa]]||fa%c==1) bz[dfn[i]]=true;
int j=head[i];
while (j>0) {
last=t;
if (dg(q[j][1],i)) {
size[dfn[i]]+=size[dfn[q[j][1]]];
re=true;
}
else {
for (int k=last+1;k<=t;k++) bz[k]=false;
t=last;
}
j=nex[j];
}
if (i%c==1) re=true;
return re;
}
int main() {
freopen("robots.in","r",stdin);
freopen("robots.out","w",stdout);
scanf("%d %d %d\n",&r,&c,&k);
w[1]=-c; w[2]=c; w[3]=-1; w[4]=1;
tot=0;
for (i=1;i<=r;i++) {
for (j=1;j<=c;j++) {
++tot;
ch=getchar();
if (j==c) go[tot]=0;
else {
if (ch=='U') go[tot]=tot+w[1];
else if (ch=='D') go[tot]=tot+w[2];
else if (ch=='L') go[tot]=tot+w[3];
else if (ch=='R') go[tot]=tot+w[4];
}
q[tot][0]=go[tot]; q[tot][1]=tot;
}
scanf("\n");
}
for (i=1;i<=tot;i++) {
nex[i]=head[q[i][0]];
head[q[i][0]]=i;
}
j=head[0]; t=-1; dfn[0]=0; size[0]=1;
dg(0,0);
while (j>0) {
bz[dfn[q[j][1]]]=true;
j=nex[j];
}
f[1][0]=true;
for (i=1;i<=t;i++) {
for (j=0;j<=k;j++) {
if (!f[i][j]) continue;
if (j+1<=k&&!bz[i]) {
f[i+size[i]][j+1]=true;
g[i+size[i]][j+1]=i;
}
if (size[i]!=1) {
f[i+1][j]=true;
g[i+1][j]=g[i][j];
}
}
}
for (i=1;i<=t;i++) b[dfs[i]]=true;
for (i=1;i<=r;i++) {
if (!b[i*c]) {
b[i*c]=true;
ss++;
}
}
for (i=k;i>=0;i--) {
if (f[t+1][i]&&k-i<=r*c-t-ss) {
x=t+1; y=i;
while (g[x][y]!=0) {
z=dfs[g[x][y]];
printf("%d %d\n",(z-1)/c+1,(z-1)%c+1);
x=g[x][y]; y--;
}
s=k-i;
for (j=1;j<=r*c;j++) {
if (s==0) break;
if (!b[j]) {
printf("%d %d\n",(j-1)/c+1,(j-1)%c+1);
s--;
}
}
return 0;
}
}
printf("-1\n");
return 0;
}