• 博客园logo
  • 会员
  • 周边
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
The blog of EzSun
lyy && ?
博客园    首页    新随笔    联系   管理    订阅  订阅

4.25小测总结

T3

一道状压 \(dp\) 唐题,但赛时太困了没切出来。
不难发现可以设 \(f_{i,S}\) 为考虑前 \(i\) 个动物,\([i,i+5)\) 区间的动物选取状态为 \(S\) 的答案(低位代表 \(i\))。
先抛开这是一个环,考虑当这是一条链时,我们要怎么做。
可以预处理 \(num_{i,S}\) 为 \([i,i+5)\) 的选取状态为 \(S\) 时,所有站在 \(i\) 位置的开心细佬(hhh)的个数。
那么转移即为 $f_{i,S}=\max(f_{i-1,(S&15)<<1})+num_{i,(S\(15)<<1|1}\)。
那么再把环考虑进来,
不难想到可以枚举开始 \(5\) 个位置的状态,设其为 \(T\),那么最终の答案即为 \(f_{n,T}\)。

Code:

#include<bits/stdc++.h>
using namespace std;
const int N = 1e4 + 7, C = 5e4 + 7;
int n, c;
int num[N][40], dp[N][40];
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> n >> c;
	for(int i = 1; i <= c; i++){
		int e, f, l;
		cin >> e >> f >> l;
		int love = 0, hate = 0;
		for(int j = 1, x; j <= f; j++){
			cin >> x;
			love |= (1 << (x - e + n) % n);
		}
		for(int j = 1, x; j <= l; j++){
			cin >> x;
			hate |= (1 << (x - e + n) % n); 
		} 
		for(int j = 0; j < 32; j++){
			if((j & love) || ((~j) & hate)) num[e][j]++;
		}
	}
	int ans = 0;
	for(int T = 0; T < 32; T++){
		memset(dp[0], 128, sizeof(dp[0]));
		dp[0][T] = 0;
		for(int i = 1; i <= n; i++){
			for(int S = 0; S < 32; S++){
				dp[i][S] = max(dp[i - 1][((S & 15) << 1)], dp[i - 1][((S & 15) << 1) | 1]) + num[i][S];
			}
		}
		ans = max(ans, dp[n][T]);
	}
	printf("%d\n", ans);
	return 0;
}

T4

题解:

一道状压 dp 和树形 dp 结合的好题。
删边的操作显然是比较不好处理的,我们考虑将其转换为求最大残留边权,答案即为总边权减去最大添加边权。
对于所有非树边,如果添加其所得到的环是偶环,便可以将其直接删去,不考虑到后续的讨论范围中。
考虑两个共顶点的奇环,不难发现这两个环可以合成一个偶环,因此要求不能存在两个奇环共顶点。
这就要求最终的图为一个仅存在奇环的仙人掌图。
由于每个点的度数不超过 \(10\),状压儿子是否被考虑。
设 \(f_{i,S}\) 为以 \(i\) 为根的子树中不考虑儿子集合 \(S\) 的最大残留边权。
若不考虑非树边,则

\[f_{i,S}=\sum_{j \in son_i}^{son} f_{j,0} \]

对于每一条连接 \(u,v\) 的非树边:
设 \(t=\operatorname{lca}(u,v)\)。
此时 \(u \rightarrow t \rightarrow v \rightarrow u\) 形成了一个环。
由于不能存在两个环共顶点,因此在这个环上的点强制其不被考虑。
所以对于所有在环上的点,其父亲的贡献都为 \(f_{fa_u,u}\) 而非 \(f_{fa_u,0}\)。
设 \(u,v\) 分别在 \(t\) 的 \(x,y\) 子树上。
所以转移方程便为

\[f_{t,S} = \max(f_{t,S},f_{t,S+x+y} + \sum_{k \in path(u,v)}^{fa_k,k \neq t} f_{fa_k,k} + f_{u,0} + f_{v,0} + w_{u,v}) \]

Code:

#include<bits/stdc++.h>
using namespace std;
const int N = 1007, M = 5007, U = 1 << 10;
int n, m;
int lg[N], st[N][17], idx[N], dfn[N], cnt;
int dep[N], son[N], id[N], fa[N];
int f[N][U + 7];
struct Node{
	int x, y, w;
};
vector<Node>g[N];
struct Edge{
	int head[N], tot = 1;
	int dfn[N], idx[N], cnt;
	bool vis[M << 1];
	struct edge{
		int to, w, pre;
	}e[M << 1];
	void add(int x, int y, int z){
		e[++tot] = {y, z, head[x]};
		head[x] = tot;
	}
	void build_ST(){
		for(int j = 1; (1 << j) <= n; j++){
			for(int i = 1; i + (1 << j) - 1 <= n; i++){
				st[i][j] = min(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]);
			}
		}
	}
	int lca(int x, int y){
		if(x == y) return x;
		x = dfn[x];
		y = dfn[y];
		if(x > y) swap(x, y);
		int d = lg[y - x];
		return idx[min(st[x + 1][d], st[y - (1 << d) + 1][d])];
	}
	void dfs1(int u, int father){
		dfn[u] = ++cnt;
		idx[cnt] = u;
		st[cnt][0] = dfn[father];
		fa[u] = father;
		for(int i = head[u]; i; i = e[i].pre){
			int v = e[i].to;
			if(v == fa[u] || e[i].w) continue;
			dep[v] = dep[u] + 1;
			id[v] = son[u]++;
			dfs1(v, u);
		}
	}
	void doit(){
		for(int u = 1; u <= n; u++){
			for(int i = head[u]; i; i = e[i].pre){
				int v = e[i].to;
				if(v == fa[u] || !e[i].w  || vis[i]) continue;
				vis[i] = vis[i ^ 1] = 1;
				if((dep[u] + dep[v]) % 2 == 0){
					g[lca(u, v)].push_back(Node{u, v, e[i].w});
				}
			}
		}
	}
	void dp(int u){
		for(int i = head[u]; i; i = e[i].pre){
			int v = e[i].to;
			if(v == fa[u] || e[i].w) continue;
			dp(v);
			for(int S = 0; S < U; S++){
				if(!(S & (1 << id[v]))) f[u][S] += f[v][0];
			}
		}
		for(Node tmp : g[u]){
			int x = tmp.x, y = tmp.y, w = tmp.w;
			if(x != u){
				w += f[x][0];
				while(fa[x] != u){
					w += f[fa[x]][1 << id[x]];
				 	x = fa[x];
				}
			}
			else x = -1;
			if(y != u){
				w += f[y][0];
				while(fa[y] != u){
					w += f[fa[y]][1 << id[y]];
					y = fa[y];
				}   
			}
			else y = -1;
			int T = 0;
			if(x != -1) T |= (1 << id[x]);
			if(y != -1) T |= (1 << id[y]); 
			for(int S = 0; S < U; S++){
				if((S & T) == 0) f[u][S] = max(f[u][S], f[u][S | T] + w);
			}
		}
	}
}E;
void init(){
	for(int i = 2; i <= N - 7; i++) lg[i] = lg[i >> 1] + 1;
}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> n >> m;
	init();
	int sum = 0;
	for(int i = 1; i <= m; i++){
		int x, y, z;
		cin >> x >> y >> z;
		E.add(x, y, z);
		E.add(y, x, z);
		sum += z;
	}
	memset(st, 0x3f, sizeof(st));
	E.dfs1(1, 0);
	E.build_ST();
	E.doit();
	E.dp(1);
	printf("%d\n", sum - f[1][0]);
	return 0;
}
posted @ 2026-04-27 14:02  EzSun599  阅读(4)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2026
浙公网安备 33010602011771号 浙ICP备2021040463号-3