首页 > 代码库 > bzoj 1040: [ZJOI2008]骑士 環套樹DP
bzoj 1040: [ZJOI2008]骑士 環套樹DP
1040: [ZJOI2008]骑士
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 1755 Solved: 690
[Submit][Status]
Description
Z国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英。他们劫富济贫,惩恶扬善,受到社会各界的赞扬。最近发生了一件可怕的事情,邪恶的Y国发动了一场针对Z国的侵略战争。战火绵延五百里,在和平环境中安逸了数百年的Z国又怎能抵挡的住Y国的军队。于是人们把所有的希望都寄托在了骑士团的身上,就像期待有一个真龙天子的降生,带领正义打败邪恶。骑士团是肯定具有打败邪恶势力的能力的,但是骑士们互相之间往往有一些矛盾。每个骑士都有且仅有一个自己最厌恶的骑士(当然不是他自己),他是绝对不会与自己最厌恶的人一同出征的。战火绵延,人民生灵涂炭,组织起一个骑士军团加入战斗刻不容缓!国王交给了你一个艰巨的任务,从所有的骑士中选出一个骑士军团,使得军团内没有矛盾的两人(不存在一个骑士与他最痛恨的人一同被选入骑士军团的情况),并且,使得这支骑士军团最具有战斗力。为了描述战斗力,我们将骑士按照1至N编号,给每名骑士一个战斗力的估计,一个军团的战斗力为所有骑士的战斗力总和。
Input
第一行包含一个正整数N,描述骑士团的人数。接下来N行,每行两个正整数,按顺序描述每一名骑士的战斗力和他最痛恨的骑士。
Output
应包含一行,包含一个整数,表示你所选出的骑士军团的战斗力。
Sample Input
10 2
20 3
30 1
Sample Output
HINT
对于100%的测试数据,满足N ≤ 1 000 000,每名骑士的战斗力都是不大于 1 000 000的正整数。
Source
╮(╯▽╰)╭ 網上說什麼外向樹內向樹,完全不懂,說白了,這道題就是一個環套樹DP,由於每個人只貢獻一條邊,所以最終圖的結構只會是一系列包含一個環的聯通快,在這些聯通塊上分別做環套樹DP就行了。
這道題環套樹DP非常簡單,直接隨機選環上一條邊刪掉,然後特殊處理刪掉邊的兩個端點,使之不被選擇,然後在樹上DP即可。
這是我第一次編環套樹。
#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<ctime>#include<cmath>#include<algorithm>#include<set>#include<map>#include<vector>#include<string>#include<queue>using namespace std;#ifdef WIN32#define LL "%I64d"#else#define LL "%lld"#endif#define MAXN 1100000#define MAXV MAXN*2#define MAXE MAXV*2#define INF 0x3f3f3f3f#define INFL 0x00003f3f3f3f3f3fLLtypedef long long qword;inline int nextInt(){ char ch; int x=0; bool flag=false; do ch=getchar(),flag=(ch==‘-‘)?true:flag; while(ch<‘0‘||ch>‘9‘); do x=x*10+ch-‘0‘; while (ch=getchar(),ch<=‘9‘ && ch>=‘0‘); return x*(flag?-1:1);}int n,m;int e[MAXN][3];struct Edge{ int np; Edge *next;}E[MAXE],*V[MAXV];int tope=-1;void addedge(int x,int y){ E[++tope].np=y; E[tope].next=V[x]; V[x]=&E[tope];}int uf[MAXN];int get_fa(int now){ return (now==uf[now])?now:(uf[now]=get_fa(uf[now]));}bool comb(int x,int y){ x=get_fa(x); y=get_fa(y); if (x==y)return false; uf[x]=y; return true;}int roof[MAXN],topr=-1;int state[MAXN];int ptr[MAXN];int v[MAXN];pair<qword,qword> dfs(int now,int f){ pair<qword,qword> ret,t; Edge *ne; ret=make_pair(v[now],0); for (ne=V[now];ne;ne=ne->next) { if (ne->np==f)continue; t=dfs(ne->np,now); ret.second+=max(t.first,t.second); ret.first+=t.second; } if (state[now])ret.first=-INFL; return ret;}int main(){ int i,j,k; int x,y,z; qword ans=0; scanf("%d",&n); memset(ptr,-1,sizeof(ptr)); for (i=0;i<=n;i++)uf[i]=i; for (i=0;i<n;i++) { scanf("%d%d",&v[i+1],&e[i][1]); e[i][0]=i+1; if (!comb(e[i][0],e[i][1])) { roof[++topr]=e[i][0]; e[i][2]=true; ptr[e[i][0]]=e[i][1]; }else { addedge(e[i][0],e[i][1]); addedge(e[i][1],e[i][0]); } } for (i=0;i<=topr;i++) { state[get_fa(roof[i])]=true; } for (i=1;i<=n;i++) { if (!state[get_fa(i)]) { state[get_fa(i)]=true; roof[++topr]=get_fa(i); } } memset(state,0,sizeof(state)); pair<qword,qword> pr; qword t; for (i=0;i<=topr;i++) { if (ptr[roof[i]]==-1) { pr=dfs(roof[i],roof[i]); ans+=max(pr.first,pr.second); }else { //state[roof[i]]=true; state[ptr[roof[i]]]=true; pr=dfs(roof[i],roof[i]); t=max(pr.first,pr.second); state[ptr[roof[i]]]=false; pr=dfs(roof[i],roof[j]); t=max(t,pr.second); ans+=t; } } printf(LL"\n",ans); return 0;}
bzoj 1040: [ZJOI2008]骑士 環套樹DP