[2017CCPC杭州] HDU 6268 点分治+bitset

给出一个 n ≤ 3000 n\leq3000 n3000个点的树,树上每个结点都有 w i ≤ 1 e 5 w_i\leq1e5 wi1e5的点权。现在要求对于 ∀ i ∈ [ 1 , m ] \forall i \in [1,m] i[1,m]所有的数,是否存在一个连通子图满足点权和等于 i i i m ≤ 1 e 5 m\leq1e5 m1e5
因为值域范围不会超过 1 e 5 1e5 1e5,给每个点开一个 b i t s e t bitset bitset b i t s e t bitset bitset的每一位就代表这个值是否存在,暴力转移 b i t s e t bitset bitset
避免退化成链,考虑对重心分治,复杂度为 O ( n 2 l o g n / w ) O(n^2logn/w) O(n2logn/w)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int N=3e3+7;
bitset<100001> f[N];  
bitset<100001> ans;
vector<int> go[N]; 
bool used[N];
int w[N],mx[N],sz[N];
int tot,rt;
void getrt(int u,int fa) {
	mx[u]=0;sz[u]=1;
	for(auto &v:go[u]) {
		if(used[v]) continue;
		if(v==fa) continue;
		getrt(v,u);
		mx[u]=max(mx[u],sz[v]);
		sz[u]+=sz[v];
	}
	mx[u]=max(mx[u],tot-sz[u]);
	if(mx[u]<mx[rt]) rt=u;
}
void dfs(int u,int fa) {
	f[u]<<=w[u];
	sz[u]=1;
	for(auto &v:go[u]) {
		if(used[v]) continue;
		if(v==fa) continue;
		f[v]=f[u];
		dfs(v,u);
		sz[u]+=sz[v];
		f[u]|=f[v];
	}
}
void solve(int u) {
	used[u]=1;
	f[u]=1;
	dfs(u,0);
	ans|=f[u];
	for(auto &v:go[u]) {
		if(!used[v]) {
			tot=sz[v];
			mx[rt=0]=1e9;
			getrt(v,u);
			solve(rt);
		}
	}
}
int main() {
	int T;
	scanf("%d",&T);
	while(T--) {
		memset(used,0,sizeof(used)); 
		ans.reset();
		int n,m;
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;i++) 
			go[i].clear();
		for(int i=1;i<n;i++) {
			int u,v;
			scanf("%d%d",&u,&v);
			go[u].push_back(v);	
			go[v].push_back(u);	
		}
		for(int i=1;i<=n;i++)
			scanf("%d",&w[i]);
		rt=0;tot=n;mx[rt]=1e9;
		getrt(1,0);
		solve(rt); 
		for(int i=1;i<=m;i++)
			printf("%d",(int)ans[i]); 
		printf("\n"); 
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值