【[APIO2010]巡邏】
阿新 • • 發佈:2019-01-01
\(APIO\)的題就是非常難啊
首先看到\(k=1\)的情況,顯然我們只需要找到一條直徑把這條直徑的兩端連起來就好了
因為我們連這一條新邊的實質是使得這一條鏈上的邊不需要重複經過了,我們想讓走的邊儘量少,自然需要重複經過的儘量少,所以\(k=1\)找到直徑就好了
答案就是\(2\times(n-1)-R+1\),\(R\)是直徑的長度,\(+1\)是因為多了一條邊要走
之後是\(k=2\)的情況
有了上面的經驗可能第一感受就是在找一條儘量長的路徑,使得這條路徑上的邊只需要經過一次就好了
但是有一些邊非常特殊,就是那些已經在第一次被走過的邊,由於每一條邊都必須被經過一次,如果有一條邊在兩次選擇的都出現了,我們顯然不能將這條邊的出現次數變成\(0\)
好像這個樣子不僅沒有什麼貢獻反而使得這條邊多走了一次,所以這條邊實際上的邊權應該是\(-1\)
所以我們把第一條路徑上的所有邊搞成\(-1\)就好了,之後再求一遍直徑直徑就好了
程式碼
由於這次有了負數,所以\(dfs\)求直徑就掛了,只能換根\(dp\)求直徑了
#include<iostream> #include<queue> #include<cstring> #include<cstdio> #define LL long long #define re register #define maxn 100005 #define max(a,b) ((a)>(b)?(a):(b)) inline int read() { char c=getchar(); int x=0; while(c<'0'||c>'9') c=getchar(); while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar(); return x; } struct E { int v,nxt,w; }e[maxn<<1]; int head[maxn],deep[maxn],pre[maxn]; int dp[2][maxn],f[maxn],vis[maxn]; int n,m,S,num=1,R,ans; int now,t; inline void add_edge(int x,int y) { e[++num].v=y; e[num].w=1; e[num].nxt=head[x]; head[x]=num; } void up(int x) { for(re int i=head[x];i;i=e[i].nxt) if(!deep[e[i].v]) { deep[e[i].v]=deep[x]+1; up(e[i].v); if(dp[0][e[i].v]+e[i].w>dp[0][x]) dp[1][x]=dp[0][x],dp[0][x]=e[i].w+dp[0][e[i].v]; else dp[1][x]=max(dp[0][e[i].v]+e[i].w,dp[1][x]); } } void down(int x) { R=max(R,dp[0][x]+f[x]); R=max(R,dp[1][x]+dp[0][x]); for(re int i=head[x];i;i=e[i].nxt) if(deep[e[i].v]>deep[x]) { if(dp[0][e[i].v]+e[i].w==dp[0][x]) f[e[i].v]=max(f[x],dp[1][x])+e[i].w; else f[e[i].v]=max(f[x],dp[0][x])+e[i].w; down(e[i].v); } } void dfs(int x) { for(re int i=head[x];i;i=e[i].nxt) if(!deep[e[i].v]) { deep[e[i].v]=deep[x]+1; dfs(e[i].v); } } void find_route(int x) { if(!pre[x]) return; e[pre[x]].w=-1; e[pre[x]^1].w=-1; find_route(e[pre[x]^1].v); } void BFS() { std::queue<int> q; q.push(now); vis[now]=1; while(!q.empty()) { int k=q.front(); q.pop(); for(re int i=head[k];i;i=e[i].nxt) if(!vis[e[i].v]) { pre[e[i].v]=i; if(e[i].v==t) { find_route(t); return; } vis[e[i].v]=1; q.push(e[i].v); } } } inline void get_R() { now=0; for(re int i=1;i<=n;i++) if(deep[i]>deep[now]) now=i; memset(deep,0,sizeof(deep)); deep[now]=1; dfs(now); t=0; for(re int i=1;i<=n;i++) if(deep[i]>deep[t]) t=i; R=deep[t]-1; BFS(); } int main() { n=read(),S=read(); int x,y,z; for(re int i=1;i<n;i++) x=read(),y=read(),add_edge(x,y),add_edge(y,x); deep[1]=1; dfs(1); memset(dp[1],-20,sizeof(dp[1])); get_R(); if(S==1) { printf("%d\n",2*(n-1)-R+1); return 0; } ans=2*(n-1)-R+1; R=0; memset(deep,0,sizeof(deep)); deep[1]=1; up(1),down(1); printf("%d\n",ans-R+1); return 0; }