1. 程式人生 > >bzoj 1912: [Apio2010]patrol 巡邏【不是dp是枚舉+堆】

bzoj 1912: [Apio2010]patrol 巡邏【不是dp是枚舉+堆】

const ostream 不是dp zoj 註意 clu struct %d queue

我是智障系列。用了及其麻煩的方法= =其實樹形sp就能解決
設直徑長度+1為len(環長)
首先k=1,直接連直徑兩端就好,答案是2*n-len
然後對於k=2,正常人的做法是樹形dp:先求直徑,然後把樹的直徑上的所有邊權標為-1,再求一次直徑設新直徑+1為len2,答案是2*(n?1)?len?len2。
然後zz的做法是分兩種情況:
len=n,直接輸出n+1(因為要加個自環)
否則,答案可能從兩種情況產生:
新選出的鏈兩端在都原直徑環某一個節點下面,這樣的情況可以直接求這個節點子樹的直徑+1為mx,用2*n-len-mx+2(化簡後)
或者要經過一段原直徑dis,註意新加的邊不算,用一個優先隊列維護直徑上第j個的最大深度mx,按mx+j排序,每次掃到一個點j,用2*n-len+3-(q.top().first+mx-j)更新答案即可

#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;
const int N=100005,inf=1e9;
int n,m,h[N],cnt,de[N],mx,s,t,fa[N],len,q[N],top,ans=inf,p[N];
bool v[N];
struct qwe
{
    int ne,to;
}e[N<<1];
int read()
{
    int r=0,f=1;
    char p=getchar();
    while(p>‘9‘
||p<‘0‘) { if(p==‘-‘) f=-1; p=getchar(); } while(p>=‘0‘&&p<=‘9‘) { r=r*10+p-48; p=getchar(); } return r*f; } void add(int u,int v) { cnt++; e[cnt].ne=h[u]; e[cnt].to=v; h[u]=cnt; } void dfs(int u,int fat,int
len) { fa[u]=fat; if(len>mx) mx=len,s=u; for(int i=h[u];i;i=e[i].ne) if(e[i].to!=fat&&!v[e[i].to]) dfs(e[i].to,u,len+1); } int main() { n=read(),m=read(); for(int i=1;i<n;i++) { int x=read(),y=read(); add(x,y),add(y,x); } dfs(1,0,1); t=s; mx=0; dfs(s,0,1); len=mx; if(m==1) { printf("%d\n",2*n-len); return 0; } if(len==n) { printf("%d\n",n+1); return 0; } for(int x=s;x;x=fa[x]) v[x]=1,q[++top]=x;//,cerr<<x<<" ";cerr<<endl; priority_queue<pair<int,int> >qq; for(int j=1;j<=top;j++) { mx=0; dfs(q[j],0,1); if(mx>1) { if(!qq.empty()) ans=min(ans,2*n-len+3-(qq.top().first+mx-j)); qq.push(make_pair(mx+j,j)); } } for(int j=1;j<=top;j++) { mx=0; dfs(q[j],0,1); if(mx==1) continue; v[q[j]]=0; mx=0;//cerr<<q[j]<<" "<<s<<" "; dfs(s,0,1);//cerr<<mx<<endl; ans=min(ans,2*n-len-mx+2); v[q[j]]=1; } printf("%d\n",ans); return 0; }

bzoj 1912: [Apio2010]patrol 巡邏【不是dp是枚舉+堆】