ZJOI2008 骑士
这道题一开始看没什么头绪……之后觉得不妨把骑士向自己痛恨的那个人连一条边,那么我们好像就转化成了一个取父亲就不能取儿子这么一个操作。
非常的像那个没有上司的舞会。
不过这题有一些bug,就是在一些联通块中可能存在环。不过我们仔细想一下之后会发现,因为每个点的出度都是1,所以如果骑士之间的厌恶情况成了一个环,那么肯定是一个简单环,也就是一个连一个,最后头和尾相连的情况,否则就不满足每个点的出度都是1了。同样的,一个联通块中也不可能出现两个及以上的环。
这样我们找到所有的联通块中的环,之后把环上的任意一条边断开,之后就肯定成为了一棵树。之后直接做树型DP即可。不过注意这里断边之后会出现两个新的点,第一种情况是要强制性选其中之一,第二种情况就强制性选另一个,直接在上面进行树型DP即可。
因为图不一定是完全联通的,所以我们像tarjan一样枚举每一个点dfs,如果这个点被搜过,那么我们直接跳过。最后把所有联通块内部的答案加起来就是最终的结果。
注意这里求一个联通块的环……因为后面DP方便我们还是选择了建立无向图。之后就跟着dalao学了一手怎么用位运算判断环……
我们像跑网络流的时候一样,把ecnt的初始值设为-1,这样加入的两条边就可以通过^1来进行直接转移。之后在每次继续向下dfs的时候,把上次走的那条边也一起传下去,这样就可以如果当前的边^1是上一条边,就说明这条边已经走过了,那么直接继续。然后如果找到了环,我们记录一下两个端点的位置之后开始DP就可以了。
然后在DP的时候,如果当前边或者其^1的值是你断过的那条边的边号,就直接继续。
注意find的时候初始要传-2,因为-1^1 = -2.
具体细节看一下代码。
#include<iostream> #include<cstdio> #include<cmath> #include<algorithm> #include<queue> #include<cstring> #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) #define enter putchar('\n') using namespace std; typedef long long ll; const int M = 1000005; ll n,s,tot,x1,x2,edg,dp[M][2],q[M],head[M],a,b,ans,ecnt = -1; bool vis[M]; ll read() { ll ans = 0,op = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') op = -1; ch = getchar(); } while(ch >='0' && ch <= '9') { ans *= 10; ans += ch - '0'; ch = getchar(); } return ans * op; } struct node { ll next,to; }e[M<<1]; void add(ll x,ll y) { e[++ecnt].to = y; e[ecnt].next = head[x]; head[x] = ecnt; } void find(ll x,ll pre) { vis[x] = 1; for(int i = head[x];i != -1;i = e[i].next) { if((i^1) == pre) continue; if(vis[e[i].to]) { x1 = x,x2 = e[i].to,edg = i; continue; } find(e[i].to,i); } } void dfs(ll x,ll pre) { dp[x][0] = 0,dp[x][1] = q[x]; for(int i = head[x];i != -1;i = e[i].next) { if((i^1) == pre) continue; if(i == edg || (i^1) == edg) continue; dfs(e[i].to,i); dp[x][1] += dp[e[i].to][0]; dp[x][0] += max(dp[e[i].to][1],dp[e[i].to][0]); } } int main() { memset(head,-1,sizeof(head)); n = read(); rep(i,1,n) a = read(),b = read(),add(i,b),add(b,i),q[i] = a; ans = 0; rep(i,1,n) { if(vis[i]) continue; find(i,-2); dfs(x1,-1); ll cur = dp[x1][0]; dfs(x2,-1); cur = max(cur,dp[x2][0]); ans += cur; } printf("%lld\n",ans); return 0; }
当你意识到,每个上一秒都成为永恒。