20150807解题报告

【T1】way

写了个hash+map的优美状压,改写成meet in the middle可以满分。【我说的是人话吗?】

100%题解:对于一条哈密尔顿回路,除了1外另找一个点i则该路径为1-……-i-……-1。记前半段除了1与i长度为n1,后半段为n2,那么如果确定了i,只要满足前半段与后半段经过的点不重复(除1)且路径总长为l。具体来说就是就是枚举一个点i,用P(n-2,n1)的时间枚举前半段的所有情况,用hash记下来(拉链,c++用map估计会T),再用P(n-2,n2)的时间枚举后半段的所有情况,并在hash中找到与之对应的前半段,统计进答案中。这样的话复杂度为(n-1)*(P(n-2,n1)+P(n-2,n2))*(hash均摊复杂度),显然n1取trunc(n/2)最优。

70分代码:

#include <bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define red(i, a, b) for(int i = (a); i >= (b); i--)
#define ll long long

typedef map<int, int> mp;
typedef mp::iterator mpit;
const int maxn = 20;
const int NN = 17000;
int a[maxn][maxn];
int vis[maxn];
int n, l, cnt;
mp f[NN][maxn];

void dfs(int dep, int x, int now) {
	vis[x] = 1;
	if (dep == n) {
		if (now + a[x][0] == l) cnt++;
	}
	else
		rep(i, 1, n-1) {
			if (vis[i]) continue;
			if (now + a[x][i] > l) continue;
			dfs(dep + 1, i, now + a[x][i]);
		}
	vis[x] = 0;
}

int main() {
//	freopen("way.in", "r", stdin);
//	freopen("way.out", "w", stdout);
	scanf("%d%d", &n, &l);
	rep(i, 0, n-1) rep(j, 0, n-1) scanf("%d", &a[i][j]);
	cnt = 0;
	if (n <= 11) {
		memset(vis, 0, sizeof(vis));
		dfs(1, 0, 0);
		printf("%d\n", cnt);
	} else {
		int jj = 0;
		for(int i = 2; i <= (1 << (n - 1)) ; i <<= 1) {
			jj++;
			f[i][jj][a[0][jj]] = 1; 
		}
		for(int i = 2; i <= (1 << n) - 2; i += 2) {
			for(int j = 1; j <= n-1; j++) {
				//cout << i << ' ' << j << ' ' << (i & (1<<j) )<< endl;
				if ((i & (1<<j))== 0) continue;
				for(int k = 1; k <= n-1; k++) {
					if (k == j) continue;
					int state = i - (1 << j);
					for(mpit it = f[state][k].begin(); it != f[state][k].end(); ++it) {
						int len = it -> first;
						f[i][j][len + a[k][j]] += it -> second;
					}
				}
			}
		}
		int final = (1 << n) - 2;
		for(int i = 1; i <= n-1; i++) {
			cnt += f[final][i][l-a[i][0]];
		}
		printf("%d\n", cnt);
	}
	return 0;	
}

【T2】Tree

上下两个方向树形dp,给小朋友讲做法真的好累啊。【我说的是人话吗】

100%题解:先用一遍dfs求出以1(这个点随便取)为根往下的最大长度与次大长度,再用一遍dfs求出以1(与之前的根保持一致)为根每个点的不进入该点子树的最大长度(dfs到一个点u时用u以及u的儿子的信息更新u的儿子的信息),再用每个点的三个值求每个点的答案

(附上大师的神图)



#include <bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define red(i, a, b) for(int i = (a); i >= (b); i--)
#define ll long long

const int maxn = 200000;
struct edge {
	int from, to, len, nxt;
}e[maxn];
struct dp {
	int len, ch;
}f[maxn], g[maxn];
int vis[maxn], head[maxn], order[maxn], fa[maxn], dis[maxn], h[maxn], ans[maxn];
int n, tail = 0;
queue<int> q;

void addedge(int u, int v, int w) {
	e[++tail].from = u;
	e[tail].to = v;
	e[tail].len = w;
	e[tail].nxt = head[u];
	head[u] = tail;
}

void bfs() {
	int m = 1; order[1] = 1; 
	memset(vis, 0, sizeof(vis));
	q.push(1); vis[1] = 1;
	fa[1] = 0; dis[1] = 0;
	while(!q.empty()) {
		int u = q.front(); q.pop();
		for(int i = head[u]; i != -1; i = e[i].nxt) {
			int v = e[i].to;
			if (vis[v]) continue;
			q.push(v); vis[v] = 1;
			order[++m] = v;
			fa[v] = u; dis[v] = e[i].len;
		}
	}
}

void DP_UP() {
	red(i, n, 1) {
		int u = order[i], max1 = 0, max2 = 0;
		f[u].len = 0; f[u].ch = 0;
		g[u].len = 0; f[u].ch = 0;
		for(int j = head[u]; j != -1; j = e[j].nxt) {
			int v = e[j].to;
			if (v == fa[u]) continue;
			if (f[v].len + e[j].len > f[u].len) {
				g[u].len = f[u].len;
				g[u].ch = f[u].ch;
				f[u].len = f[v].len + e[j].len;
				f[u].ch = v;
			}else if (f[v].len + e[j].len > g[u].len) {
				g[u].len = f[v].len + e[j].len;
				g[u].ch = v;
			}
		}
	}
}

void DP_DOWN() {
	rep(i, 1, n) {
		int u = order[i];
		if (u == 1) {
			h[i] = 0;
			continue;
		}
		h[u] = f[fa[u]].ch == u ? g[fa[u]].len : f[fa[u]].len;
		if (h[fa[u]] > h[u]) h[u] = h[fa[u]];
		h[u] += dis[u];
	}
}

void Calc() {
	rep(i, 1, n) {
		int u = order[i];
		ans[u] = f[u].len + g[u].len;
		if (f[u].len + h[u] > ans[u]) ans[u] = f[u].len + h[u];
	}
	rep(i, 1, n) printf("%d\n", ans[i]);
}

int main() {
//	freopen("tree.in", "r", stdin);
//	freopen("tree.out", "w", stdout);

	scanf("%d", &n);
	rep(i, 1, n) head[i] = -1;
	rep(i, 1, n-1) {
		int u, v, w;
		scanf("%d%d%d", &u, &v, &w);
		addedge(u, v, w);
		addedge(v, u, w);
	}
	bfs();
	DP_UP();
	DP_DOWN();
	Calc();
	return 0;	
}

【T3】Photo

这dp真TMD太NB啦,还用了一次不定方程的解得个数,大师不愧数学帝啊。【我说的是人话吗?】

100%题解:


#include <bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for (int i = (a); i <= (b); i++)
#define red(i, a, b) for (int i = (a); i >= (b); i--)
#define ll long long

const ll mod = 1000000007;
const int maxn = 60;
const int maxp = 60;
const int maxtot = 6000;
ll dp[maxn][maxtot], c[maxtot][maxtot];
ll fac[maxp], a[maxn];
int n;
ll sum,ans;

void Calc_C() {
	fac[0] = 1; c[0][0] = 1;
	for(ll i = 1; i <= maxp; i++) fac[i] = fac[i-1] * i % mod;
	rep(i, 1, 5000) {
		c[i][0] = 1; c[i][i] = 1;
		rep(j, 1, i-1) {
			c[i][j] = (c[i-1][j] + c[i-1][j-1]) % mod;
		}
	}
}

int main() {
	freopen("photo.in", "r", stdin);
	freopen("photo.out", "w", stdout);

	Calc_C();
	scanf("%d", &n);
	rep(i, 1, n) scanf("%I64d", &a[i]);
	memset(dp, 0, sizeof(dp));
	dp[1][a[1] - 1] = 1;
	sum = a[1];
	rep(i, 2, n) {
		rep(j, 0, sum) {
			if (dp[i-1][j] == 0) continue;
			rep(k, 1, a[i]) {
				rep(t, 0, min(j, k)) {
					ll temp = dp[i-1][j] * c[j][t] % mod * c[a[i]-1][k-1] % mod * c[sum+1-j][k-t] % mod;
					dp[i][j-t+a[i]-k] += temp; dp[i][j-t+a[i]-k] %= mod;
				}
			}
		}
		sum += a[i];
	}
	ans = dp[n][0];
	rep(i, 1, n) ans = ans * fac[a[i]] % mod;
	cout << ans << endl;
	return 0;
}

大师~~我做题一点都不愉快


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值