题解 回文

传送门

这题不会做,太丢人了

考场上试图合并路径什么的,毫无进展
想DP但完全不知道怎么定义
但正解其实就是DP

  • 关于图上回文路径的一类处理方法:
    考虑从起点和终点同时开始DP,回文的性质可以用长度+转移保证
    具体的,令 \(f_{i, j, k}\) 为走了 \(i\) 步,其中从起点向下走了 \(j\) 步,从终点向上走了 \(k\) 步的方案数
    然后转移考虑下一个落脚点的权值是否相等,若相等再转移

于是可以BFS实现,复杂度 \(O(n^3)\)
但是这样就可以通过这题了吗?
注意到 \(n=500\),在某TLEcoders上显然是过不去的
然后有一个写法:
BFS中产生了一些很难剪掉的冗余状态,且常数较大
这整个过程都可以用循环一遍DP完成,常数极小

Code:
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 502
#define ll long long
#define ull unsigned long long
#define fir first
#define sec second
#define make make_pair
//#define int long long

int n, m;
char mp[N][N];
const int mod=993244853;
inline void md(int& a, int b) {a+=b; a=a>=mod?a-mod:a;}

namespace force{
	int lim;
	ll ans;
	const ull base=13131;
	// unordered_map<ull, ll> mp1, mp2;
	int tot1, tot2;
	pair<ull, pair<int, int>> mp1[1000100], mp2[1000100];
	void dfs1(int u, int v, int len, ull pre) {
		// cout<<"dfs1: "<<u<<' '<<v<<endl;
		pre=pre*base+mp[u][v];
		if (len+1>lim) mp1[++tot1]=make(pre, make(u, v));
		else {
			if (u+1<=n) dfs1(u+1, v, len+1, pre);
			if (v+1<=m) dfs1(u, v+1, len+1, pre);
		}
	}
	void dfs2(int u, int v, int len, ull pre) {
		pre=pre*base+mp[u][v];
		if (len+1>lim) mp2[++tot2]=make(pre, make(u, v));
		else {
			if (u-1>=1) dfs2(u-1, v, len+1, pre);
			if (v-1>=1) dfs2(u, v-1, len+1, pre);
		}
	}
	bool check(pair<int, int> a, pair<int, int> b) {
		if (a.fir+1==b.fir && a.sec==b.sec) return 1;
		else if (a.fir==b.fir && a.sec+1==b.sec) return 1;
		else return 0;
	}
	void solve() {
		lim=(n+m-1)/2;
		dfs1(1, 1, 1, 0);
		dfs2(n, m, 1, 0);
		sort(mp1+1, mp1+tot1+1); sort(mp2+1, mp2+tot2+1);
		for (int i=1; i<=tot1; ++i) {
			for (int j=1; j<=tot2; ++j) if (mp1[i].fir==mp2[j].fir)
				if (check(mp1[i].sec, mp2[j].sec)) ++ans;
		}
		printf("%lld\n", ans);
		exit(0);
	}
}

namespace task1{
	int lim;
	ll ans;
	const ull base=13131;
	// unordered_map<ull, ll> mp1, mp2;
	int tot1, tot2;
	vector<char> tem, t2;
	void check() {
		t2=tem;
		reverse(t2.begin(), t2.end());
		if (t2==tem) ++ans;
	}
	void dfs1(int u, int v) {
		// cout<<"dfs1: "<<u<<' '<<v<<endl;
		tem.push_back(mp[u][v]);
		if (u==n && v==m) {
			check();
			tem.pop_back();
			return ;
		}
		if (u+1<=n) dfs1(u+1, v);
		if (v+1<=m) dfs1(u, v+1);
		tem.pop_back();
	}
	void solve() {
		dfs1(1, 1);
		printf("%lld\n", ans);
		exit(0);
	}
}

namespace task2{
	int dp[N][N][N], ans;
	bool vis[N][N][N];
	struct tpl{
		short fir, sec, thr;
		tpl(){}
		tpl(short x, short y, short z):fir(x),sec(y),thr(z){}
		inline void build(const short& x, const short& y, const short& z){fir=x; sec=y; thr=z;}
	}q[500*500*500+1];
	int ql, qr;
	void solve() {
		short lim=(n+m-2)/2+2;
		// cout<<"lim: "<<lim<<endl;
		dp[1][1][n]=1; vis[1][1][n]=1;
		// q.push(tpl(1, 1, n));
		q[++qr]=tpl(1, 1, n);
		tpl u;
		short a, b, c, d;
		while (ql<=qr) {
			u=q[ql++];
			// cout<<"u: "<<u.fir<<' '<<u.sec<<' '<<u.thr<<endl;
			if (u.fir+u.sec>=lim) continue;
			a=u.fir, b=u.sec, c=u.thr, d=n+m-u.fir-u.sec+2-u.thr;
			// cout<<a<<' '<<b<<' '<<c<<' '<<d<<endl;
			if (a>c || b>d) continue;
			if (abs(a-c)+abs(b-d)>2*(lim-u.fir-u.sec)+1) continue;
			// cout<<"cd: "<<c<<' '<<d<<endl;
			if (a+1<=n) {
				if (mp[a+1][b]==mp[c-1][d]) {
					md(dp[a+1][b][c-1], dp[u.fir][u.sec][u.thr]);
					if (!vis[a+1][b][c-1]) q[++qr].build(a+1, b, c-1), vis[a+1][b][c-1]=1;
				}
				if (mp[a+1][b]==mp[c][d-1]) {
					md(dp[a+1][b][c], dp[u.fir][u.sec][u.thr]);
					if (!vis[a+1][b][c]) q[++qr].build(a+1, b, c), vis[a+1][b][c]=1;
				}
			}
			if (b+1<=m) {
				if (mp[a][b+1]==mp[c-1][d]) {
					md(dp[a][b+1][c-1], dp[u.fir][u.sec][u.thr]);
					if (!vis[a][b+1][c-1]) q[++qr].build(a, b+1, c-1), vis[a][b+1][c-1]=1;
				}
				if (mp[a][b+1]==mp[c][d-1]) {
					md(dp[a][b+1][c], dp[u.fir][u.sec][u.thr]);
					if (!vis[a][b+1][c]) q[++qr].build(a, b+1, c), vis[a][b+1][c]=1;
				}
			}
		}
		if ((n+m)&1) {
			for (int a=1; a<=n; ++a) {
				int b=lim-a, c, d;
				c=a+1, d=b;
				if (c<=n) md(ans, dp[a][b][c]);
				c=a, d=b+1;
				if (d<=m) md(ans, dp[a][b][c]);
			}
		}
		else {
			for (int a=1; a<=n; ++a) {
				int b=lim-a, c, d;
				c=a, d=n+m-a-b+2-c;
				// cout<<"test: "<<a<<' '<<b<<' '<<c<<' '<<d<<endl;
				if (a==c && b==d) md(ans, dp[a][b][c]);
			}
		}
		printf("%d\n", ans);
		exit(0);
	}
}

namespace task{
	int dp[N][N][N], ans;
	void solve() {
		int lim=(n+m-2)/2+2;
		if (mp[1][2]==mp[n][m-1]) dp[1][0][0]=1;
		if (mp[1][2]==mp[n-1][m]) dp[1][0][1]=1;
		if (mp[2][1]==mp[n][m-1]) dp[1][1][0]=1;
		if (mp[2][1]==mp[n-1][m]) dp[1][1][1]=1;
		for (int i=1; i<=(n+m-2)>>1; ++i) {
			for (int j=max(0, i-m+1); j<=min(i, n-1); ++j) {
				for (int k=max(0, i-m+1); k<=min(i, n-1); ++k) {
					if (mp[1+j][1+i-j]!=mp[n-k][m-i+k]) continue;
					// cout<<j<<' '<<k<<endl;
					if (j) {
						if (k) md(dp[i][j][k], dp[i-1][j-1][k-1]);
						if (i-k) md(dp[i][j][k], dp[i-1][j-1][k]);
					}
					if (i-j) {
						if (k) md(dp[i][j][k], dp[i-1][j][k-1]);
						if (i-k) md(dp[i][j][k], dp[i-1][j][k]);
					}
					// printf("dp[%d][%d][%d]=%d\n", i, j, k, dp[i][j][k]);
				}
			}
		}
		if ((n+m)&1) for (int i=0; i<n-1; ++i) md(ans, dp[(n+m-2)/2][i][n-i-2]); //, cout<<i<<' '<<n-i-2<<' '<<dp[(n+m-2)/2][i][n-i-2]<<endl;
		// cout<<"---"<<endl;
		for (int i=0; i<n; ++i) md(ans, dp[(n+m-2)/2][i][n-i-1]); //, cout<<i<<' '<<n-i-1<<' '<<dp[(n+m-2)/2][i][n-i-1]<<endl;
		printf("%d\n", ans);
		exit(0);
	}
}

signed main()
{
	freopen("palin.in", "r", stdin);
	freopen("palin.out", "w", stdout);

	scanf("%d%d", &n, &m);
	// if ((n+m)%2==0) {puts("0"); return 0;}
	for (int i=1; i<=n; ++i) scanf("%s", mp[i]+1);
	if (mp[1][1]!=mp[n][m]) {puts("0"); return 0;}
	// force::solve();
	task::solve();

	return 0;
}
posted @ 2021-11-05 20:07  Administrator-09  阅读(0)  评论(0编辑  收藏  举报