题解 卡常题

传送门

不会二分图……于是考场上凭着对二分图残存的一点记忆认定它是个类似二分图最大匹配的dfs,然后不会写
状压有20pts

  • 发现一个小事情,clock()函数巨慢,几乎一半的时间都给它了
    所以随机化的while可以写成类似 while (cnt%100 || clock()<=1500000) 这样子

有n个点,n条边且连通的图,实际上就是有且仅有一个环的树,这个东西叫环套树,也叫基环树

  • 基环树的一大标志:有n个点,n条无向边

而在这里,每个白点连接了两个黑点,那白点的作用就和边类似
那就有n个点,n条无向边,求使每条边的两个带权端点至少有一个被选中的最小权值和
从环上删条边,能构成一棵树
注意到最终权值和只与选点的方案有关,也就是说从环上删掉哪一条边都是等价的
dfs找到一条环上的边断开,以这两个端点为根分别跑一次树形dp求最小权值和
为了保证断开的那条边也满足性质,这两个端点中也必须至少选一个
(这个卡了我半天)
取两次dp结果的较小值输出即可

Code:

#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 1000010
#define N2 2000100
#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, a, b, tot;

// 状压
namespace task1{
	int ans=INF;
	bool vis[21];
	int head[N2], size, cnt[N2], val[N2];
	struct edge{int to, next, val;}; edge* e;
	inline void add(int s, int t, int w) {edge* k=&e[++size]; k->to=t; k->next=head[s]; k->val=w; head[s]=size;}
	void solve() {
		//cout<<double(sizeof(dp))/1024/1024<<endl;
		int lim=(1<<n);
		tot=n*2;
		e = new edge[n*4+10];
		for (int i=1,v1,v2; i<=n; ++i) {
			v1=read()+n; v2=read()+n;
			add(i, v1, a); add(v1, i, a); add(i, v2, b); add(v2, i, b);
			++cnt[v1]; ++cnt[v2]; val[v1]+=a; val[v2]+=b;
		}
		for (int s=1,cnt,sum; s<lim; ++s) {
			memset(vis, 0, sizeof(vis));
			cnt=0; sum=0;
			for (int i=0; i<n; ++i) if (s&(1<<i)) {
				sum+=val[n+i+1]; if (sum>ans) goto jump;
				for (int j=head[n+i+1],v; j; j=e[j].next) {
					v = e[j].to;
					if (!vis[v]) {
						if (++cnt>=n) {ans=min(ans, sum); goto jump;}
						vis[v]=1;
					}
				}
			}
			jump: ;
		}
		printf("%d\n", ans);
		exit(0);
	}
}

namespace task2{
	int ans=INF;
	bool vis[N2];
	int now[N2], head[N2], size, cnt[N2], val[N2];
	struct edge{int to, next, val;}; edge* e;
	inline void add(int s, int t, int w) {edge* k=&e[++size]; k->to=t; k->next=head[s]; k->val=w; head[s]=size;}
	void random() {
		for (int i=1,a,b; i<=100; ++i) {
			a=rand()%n+1; b=rand()%n+1;
			swap(now[a], now[b]);
		}
	}
	void solve() {
		srand(time(0));
		tot=n*2;
		e = new edge[n*4+10];
		for (int i=1,v1,v2; i<=n; ++i) {
			v1=read()+n; v2=read()+n;
			add(i, v1, a); add(v1, i, a); add(i, v2, b); add(v2, i, b);
			++cnt[v1]; ++cnt[v2]; val[v1]+=a; val[v2]+=b;
		}
		for (int i=1; i<=n; ++i) now[i]=n+i;
		int cnt, sum, cnt2=0;
		while (clock()<=1500000) {
		//for (int i=1; i<=5242276; ++i) {
			++cnt2;
			memset(vis, 0, sizeof(bool)*(n*2+20));
			//random();
			random_shuffle(now+1, now+n+1);
			//for (int i=1; i<=n; ++i) cout<<now[i]<<' '; cout<<endl;
			cnt=0; sum=0;
			for (int i=1,t; i<=n; ++i) {
				t=now[i];
				sum+=val[t]; if (sum>ans) goto jump;
				for (int j=head[t],v; j; j=e[j].next) {
					v = e[j].to;
					if (!vis[v]) {
						if (++cnt>=n) {ans=min(ans, sum); goto jump;}
						vis[v]=1;
					}
				}
			}
			jump: ;
		}
		//cout<<"while: "<<cnt2<<endl;
		printf("%d\n", ans);
		exit(0);
	}
}

namespace task{
	int head[N], size, val[N], dp[N][2];
	bool vis[N];
	struct edge{int to, next;}; edge* e;
	inline void add(int s, int t) {edge* k=&e[++size]; k->to=t; k->next=head[s]; head[s]=size;}
	void dfs2(int u, int tage) {
		vis[u]=1;
		bool leaf=1;
		for (int i=head[u],v; i; i=e[i].next) {
			v = e[i].to;
			if (i!=tage&&!vis[v]) {
				dfs2(v, tage), leaf=0;
				dp[u][0]+=dp[v][1];
				dp[u][1]+=min(dp[v][0], dp[v][1]);
			}
		}
		dp[u][1]+=val[u];
		if (leaf) {
			dp[u][0]=0; dp[u][1]=val[u];
			return ;
		}
	}
	void dfs(int u, int fa) {
		//cout<<"dfs "<<u<<' '<<fa<<endl;
		vis[u]=1;
		for (int i=head[u],v; i; i=e[i].next) {
			v = e[i].to;
			if (v!=fa) {
				if (vis[v]) {
					//cout<<"ring: "<<u<<' '<<v<<endl;
					memset(dp, 0, sizeof(dp));
					memset(vis, 0, sizeof(vis));
					dfs2(u, i);
					int ans=dp[u][1];
					//cout<<"ans: "<<ans<<endl;
					memset(dp, 0, sizeof(dp));
					memset(vis, 0, sizeof(vis));
					dfs2(v, i);
					ans = min(ans, dp[v][1]);
					printf("%d\n", ans);
					exit(0);
				}
				dfs(v, u);
			}
		}
	}
	void solve() {
		e = new edge[n<<1|1];
		for (int i=1,u,v; i<=n; ++i) {u=read(); v=read(); add(u, v); add(v, u); val[u]+=a; val[v]+=b;}
		dfs(1, 0);
	}
}

signed main()
{
	#ifdef DEBUG
	freopen("1.in", "r", stdin);
	#endif
	
	n=read(); a=read(); b=read();
	//if (n<=20) task1::solve();
	//else task2::solve();
	task::solve();

	return 0;
}
posted @ 2021-07-13 16:33  Administrator-09  阅读(14)  评论(0编辑  收藏  举报