做题的过程还是挺曲折的。
最下的想法是每次询问的时候把每两个点打包成新的起点,跑最短路,知道走到相同的点上或者走到一条边的两边。
然后就毫无压力的T了。。。。
后来在同学的提醒下,反过来思考。将相同的点 和 同一条边上的两个点 做起点,预处理每一个状态的答案,然后直接输出即可。
还是T了一点。。。。各种卡常数。。。。还是没过。。。。
看题解,标算只是将原来的状态在拓展一下,这样可以降低转移的复杂度。
因为原来是这样转移的的d[i][j]=d[k][l]+2|存在边(i,k)(l,j)且两条边上的字母相同。之后状态记作d[i][j][0-25],表示i到j的路上出了开头多了一个字母,其余为回文串的最短路径。转移d[i][j][x]=d[k][j][26]+1|存在边(i,k,x),0<=x<26. d[i][j][26]=d[i][k][x]+1|存在边(k,j,x)
这样虽然状态数增多了,但是有用状态很少,而且转移复杂度大大减小,ac无压力了
这道题目告诉了我可以从平衡状态数和转移复杂度上优化算法,从而减少计算量。
贴上代码
#include <cstdio> #include <vector> #include <cstring> #include <algorithm> using namespace std; #define num(x,y,k) (((x)-1)*n*27+((y)-1)*27+(k)+1) char ch; int flag[405][405],p[4320005],q[4320005],d[4320005],R[4320005]; int n,m,x,y,z,i,j,k,xx,yy,ans,N,l,r; vector <int> a[405][26],b[405][26]; void print(int x){ /* int i=(x-1)/(n*27)+1; int j=(x-1)%(n*27)/27+1; int k=(x-1)%(n*27)%27; if (d[x]==0) return; if (d[x]==1){ printf("%c",flag[i][j]); return; } int kk=(p[x]-1)%(n*27)%27; printf("%c",kk+'a'); print(p[p[x]]); printf("%c",kk+'a');*/ for (R[l=1]=x;d[ R[l] ]>1;l++) R[l+1]=p[ p[R[l]] ]; for (int i=l;i>0;i--){ if (d[ R[i] ]<2) continue; int k=(p[R[i]]-1)%(n*27)%27; printf("%c",k+'a'); } if (d[R[l]]==1){ int i=(R[l]-1)/(n*27)+1; int j=(R[l]-1)%(n*27)/27+1; printf("%c",flag[i][j]); } for (int i=1;i<=l;i++){ if (d[ R[i] ]<2) continue; int k=(p[R[i]]-1)%(n*27)%27; printf("%c",k+'a'); } } int main(){ scanf("%d%d",&n,&m); for (i=1;i<=m;i++){ scanf("%d %d %c\n",&x,&y,&ch); a[x][ch-'a'].push_back(y); b[y][ch-'a'].push_back(x); flag[x][y]=ch; } memset(d,127/2,sizeof(d)); for (i=1,r=0;i<=n;i++) d[ q[++r]=num(i,i,26) ]=0; for (i=1;i<=n;i++) for (j=1;j<=n;j++) if (i!=j&&flag[i][j]) d[ q[++r]=num(i,j,26) ]=1; int len; for (l=1;l<=r;l++){ x=(q[l]-1)/(n*27)+1; y=(q[l]-1)%(n*27)/27+1; z=(q[l]-1)%(n*27)%27; if (z==26){ for (i=0;i<26;i++){ len=b[x][i].size(); for (j=0;j<len;j++) if (d[ num(b[x][i][j],y,i) ]>d[ q[l] ]+1){ d[ q[++r]=num(b[x][i][j],y,i) ]=d[ q[l] ]+1; p[ q[r] ]=q[l]; } } } else { len=a[y][z].size(); for (j=0;j<len;j++) if (d[ num(x,a[y][z][j],26) ]>d[ q[l] ]+1){ d[ q[++r]=num(x,a[y][z][j],26) ]=d[ q[l] ]+1; p[ q[r] ]=q[l]; } } } for (i=1;i<=n*n*27;i++) if (d[i]==d[0]) d[i]=-1; scanf("%d",&m); scanf("%d",&x); m--; while (m--){ scanf("%d",&y); printf("%d",d[num(x,y,26)]); if (d[num(x,y,26)]!=-1) { printf(" "); print(num(x,y,26)); } printf("\n"); x=y; } return 0; }