题解 y

传送门

考场上写的记忆化不够快……和暴力一个分

  • 如果题面里有提到类似「从点1出发」的字样,特别注意点1根本就没有连边的情况

这题写记忆化的时候是想搜出所有可能的组合,
那么对于一个点u,剩余深度为d时的可能后缀数是一定的,可以记录下来
那就开个vector记一下,访问完子节点可以sort+unique去重,于是T飞

然后首先有个小优化:
可以不试着组合前后缀枚举能构成的结果
注意到 \(d \leqslant 20\) ,还可以枚举结果状态,每次跑个check就可以了
这个后缀的记忆化优化就很有用了
而且会有不少重边什么的,可以先去重
然而然后还是会T一个点

正解用meet in middle优化了下dfs
这样每个点要记的情况从\(2^{20}\)变成\(2^{10}\),就开得下数组了
而且dfs也能快巨多

  • 所以需要爆搜某些特定组合/边权和/异或和(?)的时候可以考虑meet in middle优化下

Code:

#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 150
#define ll long long 
#define ld long double
#define usd unsigned
#define ull unsigned long long
//#define int long long 

#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
char buf[1<<21], *p1=buf, *p2=buf;
inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n, m, d, D;
int head[N], size;
int dis0[N][N], dis1[N][N];
bool edge0[105], edge1[105];
struct edge{int to, next, val;}e[100010];
inline void add(int s, int t, int w) {edge* k=&e[++size]; k->to=t; k->val=w; k->next=head[s]; head[s]=size;}

namespace force{
	bool vis[1<<21];
	void dfs(int u, int s, int d) {
		//cout<<"dfs "<<u<<' '<<s<<' '<<d<<endl;
		if (d<=0) {vis[s]=1; return ;}
		for (int i=head[u]; i; i=e[i].next) {
			dfs(e[i].to, (s<<1)|e[i].val, d-1);
		}
	}
	void solve() {
		dfs(1, 0, d);
		int lim=1<<d, ans=0;
		for (int i=0; i<lim; ++i) if (vis[i]) ++ans; //, cout<<i<<' '; cout<<endl;
		printf("%d\n", ans);
		exit(0);
	}
}

namespace task1{
	bool vis[1<<21];
	vector<int> vec[105][22];
	void dfs(int u, int d) {
		//if (clock()>800000) {cout<<(1<<D)<<endl; exit(0);}
		//cout<<"dfs "<<u<<' '<<d<<endl;
		//++cnt[u][d];
		if (vec[u][d].size()) return ;
		if (d<=1) {
			bool t[2]={0, 0};
			for (int i=head[u],v; i; i=e[i].next) {
				if (!t[e[i].val]) vec[u][d].push_back(e[i].val), t[e[i].val]=1; //, cout<<1<<endl;
				//cout<<i<<endl;
			}
			//cout<<"vec "<<u<<' '<<d<<": "; for (int i=0; i<vec[u][d].size(); ++i) cout<<vec[u][d][i]<<' '; cout<<endl;
			return ;
		}
		for (int i=head[u],v; i; i=e[i].next) {
			v = e[i].to;
			dfs(v, d-1);
			//cout<<"go "<<v<<' '<<d-1<<endl;
			for (int j=0; j<vec[v][d-1].size(); ++j) 
				vec[u][d].push_back((e[i].val<<(d-1))|vec[v][d-1][j]); //, cout<<u<<' '<<d<<' '<<(e[i].val<<(d-1))<<' '<<vec[v][d-1][j]<<endl;
		}
		sort(vec[u][d].begin(), vec[u][d].end());
		vector<int>::iterator it=unique(vec[u][d].begin(), vec[u][d].end());
		while (it!=vec[u][d].end()) vec[u][d].pop_back();
		//cout<<"vec "<<u<<' '<<d<<": "; for (int i=0; i<vec[u][d].size(); ++i) cout<<vec[u][d][i]<<' '; cout<<endl;
	}
	void solve() {
		dfs(1, d);
		//int lim=1<<n, ans=0;
		//cout<<double(sizeof(cnt))/1024/1024<<endl;
		//for (int i=0; i<lim; ++i) if (vis[i]) ++ans;
		//cout<<3<<endl;
		printf("%d\n", vec[1][d].size());
		//cout<<vec[1][d].size()<<endl;
		//for (int i=0; i<vec[1][d].size(); ++i) cout<<vec[1][d][i]<<' '; cout<<endl;
		//for (int i=1; i<=n; ++i) {for (int j=1; j<=d; ++j) cout<<cnt[i][j]<<' '; cout<<endl;}
		exit(0);
	}
}

namespace task2{
	unordered_map<int, bool> mp[105][22];
	int cnt=0;
	bool dfs(int u, int d, int s) {
		//++cnt;
		//cout<<"dfs "<<u<<' '<<d<<' '<<bitset<5>(s)<<' '<<bitset<5>(s>>(d-1))<<endl;
		if (d<=0) return 1;
		if (mp[u][d].find(s)!=mp[u][d].end()) return mp[u][d][s];
		if (s>>(d-1)) {if (!edge1[u]) return 0;}
		else {if (!edge0[u]) return 0;}
		for (int i=head[u],s1=s>>(d-1),s2=s&((1<<(d-1))-1); i; i=e[i].next) 
			if (e[i].val==s1 && dfs(e[i].to, d-1, s2)) {mp[u][d][s]=1; return 1;}
		mp[u][d][s]=0;
		return 0;
	}
	void solve() {
		int lim=1<<d, ans=0;
		for (int i=0; i<lim; ++i) 
			if (dfs(1, d, i)) ++ans;
		printf("%d\n", ans);
		//cout<<"cnt: "<<cnt<<endl;
		exit(0);
	}
}

namespace task{
	char mp[105][105][21][1<<11];
	bool dfs(int u, int to, int d, int s) {
		//cout<<"dfs "<<u<<' '<<to<<' '<<d<<' '<<s<<endl;
		if (d<=0) return u==to;
		if (mp[u][to][d][s]) return mp[u][to][d][s]>>1;
		if (s>>(d-1)) {if (!edge1[u]) return 0;}
		else {if (!edge0[u]) return 0;}
		for (int i=head[u],s1=s>>(d-1),s2=s&((1<<(d-1))-1); i; i=e[i].next) 
			if (e[i].val==s1 && dfs(e[i].to, to, d-1, s2)) {mp[u][to][d][s]=3; return 1;}
		mp[u][to][d][s]=1;
		return 0;
	}
	void solve() {
		int lim=1<<d, ans=0, l1=d/2, l2=d-d/2;
		//cout<<"l: "<<l1<<' '<<l2<<endl;
		for (int i=0; i<lim; ++i) {
			for (int j=1; j<=n; ++j) 
				if (dfs(1, j, l1, i>>l2)) 
					for (int k=1; k<=n; ++k) 
						if (dfs(j, k, l2, i&((1<<l2)-1))) {
							++ans;
							//cout<<"++ans: "<<i<<endl;
							goto jump;
						}
			jump: ;
		}
		printf("%d\n", ans);
		exit(0);
	}
}

signed main()
{
	#ifdef DEBUG
	freopen("1.in", "r", stdin);
	#endif
	bool all_zero=1, all_one=1;
	
	n=read(); m=read(); d=read(); D=d;
	for (int i=1,u,v,c; i<=m; ++i) {
		u=read(); v=read(); c=read();
		//add(u, v, c); add(v, u, c);
		if (c) dis1[u][v]=dis1[v][u]=1;
		else dis0[u][v]=dis0[v][u]=1;
		if (c) edge1[u]=edge1[v]=1;
		else edge0[u]=edge0[v]=1;
		if (c) all_zero=0;
		else all_one=0;
	}
	for (int i=1; i<=n; ++i) for (int j=i; j<=n; ++j) if (dis1[i][j]) add(i, j, 1), add(j, i, 1);
	for (int i=1; i<=n; ++i) for (int j=i; j<=n; ++j) if (dis0[i][j]) add(i, j, 0), add(j, i, 0);
	if (!head[1]) {puts("0"); return 0;}
	if (all_zero || all_one) {puts("1"); return 0;}
	//if (d<=5) force::solve();
	//else task1::solve();
	task::solve();

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