1. 程式人生 > 其它 >Max Flow P

Max Flow P

樹上差分學習,為了做大根堆刷了這麼一道樹上差分板子題

//Max Flow P
//用來練練樹上差分
//最後答案只能用遍歷
//lca的話用tarjan吧
//算了,tarjan必須離線,用倍增吧
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
const int mx=500000+100;
bool vis[mx];
int len,n,k,head[mx];
int du[mx],kk,tim;
int f[mx][20]; int dif[mx]; int ans; int an[mx]; struct Node{ int from; int to; int next; }e[mx*3]; inline int read(){ int w=1; int x=0; char ch=getchar(); while(isdigit(ch)==false){ if(ch=='-')w=-1; ch=getchar(); } while(isdigit(ch)==true){ x
=(x<<3)+(x<<1)+(ch^48); ch=getchar(); } return x*w; } void Insert(int u,int v){ e[++len].from=u; e[len].to=v; e[len].next=head[u]; head[u]=len; } void dfs(int u){ vis[u]=1; for(int i=head[u];i;i=e[i].next){ int v=e[i].to; if(vis[v]==0
){ vis[v]=1; du[v]=du[u]+1; // printf("du[1]=%d\n",du[1]); f[v][0]=u; for(int j=1;j<=kk;++j){ f[v][j]=f[f[v][j-1]][j-1]; }//dp要從祖先那裡拿值,所以先dp再dfs dfs(v); } } } void dfss(int u){ an[u]+=dif[u]; vis[u]=1; // printf("an[%d]=%d\n",u,an[u]); for(int i=head[u];i;i=e[i].next){ int v=e[i].to; // printf("vis[%d]=%d\n",v,vis[v]); if(vis[v]==0){ vis[v]=1; dfss(v); an[u]+=an[v]; } } } int LCA(int x,int y){ // printf("du[x]=%d %d\n",du[x],du[y]); if(du[x]>du[y])swap(x,y); for(int j=kk;j>=0;--j){ // printf("%d %d %d y=%d j=%d\n",f[y][j],du[f[y][j]],du[x],y,j); if(du[f[y][j]]>=du[x])y=f[y][j];//等於號 }//把y的深度拉到與x同一水平,此刻再往上走一個就是x,y的lca //每次是log(n)級別的查詢,對了,log預設是以為底,所以要換底 // printf("y=%d\n",y); if(x==y)return x; else { for(int j=kk;j>=0;--j){ if(f[x][j]!=f[y][j]){ x=f[x][j]; y=f[y][j]; //這裡再附上那個很經典的話:為什麼不能當f[x][k]==f[y][k]直接輸出答案,因為我們求的是最近公共祖先 //注意再次體會這裡,此刻x,和y的深度一樣,大的不相等小的肯定不相等啊,如果正序,只要這次相等了後面肯定都相等 //所以正序倒序無所謂啊,我們是不等於才轉移 } } // printf("x=%d\n",x); return f[x][0]; } } void Solve(){ n=read();k=read(); kk=log(n)/log(2)+1; for(int i=1;i<=n-1;++i){ int x=read(),y=read(); Insert(x,y);//邊建立單向的應該就夠了 //是要建立雙向的,只是你以為以1為根,它給的資料可不一樣 //所以才用vis Insert(y,x); } du[1]=1; f[1][0]=1; //我覺得就是得特判1,不是 dfs(1); memset(vis,0,sizeof(vis)); for(int i=1;i<=k;++i){ int x=read(); int y=read(); int lc=LCA(x,y); // printf("lc=%d\n",lc); //差分 dif[x]++;dif[y]++;dif[lc]--;dif[f[lc][0]]-- ;//樹上差分 if(lc==1)dif[1]++; //1這裡確實應該這麼寫一下 //再次回顧定義:一個點的真實權值是一個點子樹內所有差分後的權值之和 //它這個定義不太好,我換個說法:一個點的權值是它的原本權值加上它的子樹的每個點的差分陣列的值的和(包括它本身) //這個是按點差分 //如果按邊差分: //每個節點最多隻有一個父節點,也就是向上連的邊只有一條,所以我們就把一條邊的邊權轉化成子節點的點權(根節點沒有點權) //這裡注意,每個點所代表的點權是它向上連的那一條邊,所以這裡lca-=2,lc的父親不用更改 //這個其實一手摸就理解了 } dfss(1); for(int i=1;i<=n;++i){//它有n-1個邊,n個點啊啊啊啊啊啊,你個智障 // printf("an[%d]=%d\n",i,an[i]); ans=max(ans,an[i]); } printf("%d\n",ans); } int main(){ Solve(); return 0; }
View Code