2017.10.24
今天主要進行了一些模板的復習。主要是樹剖lca方面。感覺雖然是學過的東西,不寫的話還是很容易忘的。
商務旅行
時間限制: 1 s 空間限制: 128000 KB 題目描述 Description
某首都城市的商人要經常到各城鎮去做生意,他們按自己的路線去做,目的是為了更好的節約時間。
假設有N個城鎮,首都編號為1,商人從首都出發,其他各城鎮之間都有道路連接,任意兩個城鎮之間如果有直連道路,在他們之間行駛需要花費單位時間。該國公路網絡發達,從首都出發能到達任意一個城鎮,並且公路網絡不會存在環。
你的任務是幫助該商人計算一下他的最短旅行時間。
輸入描述 Input Description輸入文件中的第一行有一個整數N,1<=n<=30 000,為城鎮的數目。下面N-1行,每行由兩個整數a 和b (1<=a, b<=n; a<>b)組成,表示城鎮a和城鎮b有公路連接。在第N+1行為一個整數M,下面的M行,每行有該商人需要順次經過的各城鎮編號。
輸出描述 Output Description在輸出文件中輸出該商人旅行的最短時間。
樣例輸入 Sample Input5
1 2
1 5
3 5
4 5
4
1
3
2
5樣例輸出 Sample Output
7
挺裸的lca問題。多的不解釋。以前用tarjan寫了,今天樹剖打了一遍。
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; int n,u,v,son[100010],fa[100010],top[100010],d[100010],head[100010],vis[100010],siz[100010],cnt,m,a,b,ans; struct edge{ int v,next; }E[150010]; void add(int u,int v){ E[++cnt].v=v; E[cnt].next=head[u]; head[u]=cnt; } voiddfs1(int x,int dep){ d[x]=dep; siz[x]=1; for(int i=head[x];i;i=E[i].next){ int v=E[i].v; if(v==fa[x])continue; fa[v]=x; dfs1(v,dep+1); siz[x]+=siz[v]; if(siz[v]>siz[son[x]])son[x]=v; } } void dfs2(int x,int tp){ top[x]=tp; if(son[x])dfs2(son[x],tp); for(int i=head[x];i;i=E[i].next){ int v=E[i].v; if(v==fa[x]||v==son[x])continue; else dfs2(v,v); } } int query(int a,int b){ while(top[a]!=top[b]){ if(d[top[a]]<d[top[b]])swap(a,b); a=fa[top[a]]; } return d[a]<d[b]?a:b; } int main(){ scanf("%d",&n); for(int i=1;i<n;i++){ scanf("%d%d",&u,&v); add(u,v);add(v,u); } dfs1(1,1); dfs2(1,1); scanf("%d",&m); m--; scanf("%d",&a); while(m--){ scanf("%d",&b); int tmp=query(a,b); ans+=d[a]+d[b]-2*d[tmp]; a=b; } printf("%d\n",ans); }
tarjan版:
#include<cstring> #include<algorithm> #include<cmath> #include<cstdio> #include <vector> using namespace std; int n,sum,x,fx,fy,m,l,r,y,a,b,ecnt,tot,head[150010],fa[150010],vis[150010],cnt; int s,t,lin[150010][2],d[150010],need,k[150100]; int ans; vector <int> ask[100100]; struct edge{ int a,b,next; }E[150010]; void add(int a,int b){ E[++ecnt].a=a; E[ecnt].b=b; E[ecnt].next=head[a]; head[a]=ecnt; } int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);} void Tarjan(int x,int dp){ d[x]=dp; for(int i=head[x];i;i=E[i].next){ if(!d[E[i].b]){ Tarjan(E[i].b,dp+1); fa[E[i].b]=x; } } int sum=ask[x].size(); for(int i=0;i<sum;i++){ int y=ask[x][i]; if(vis[y]){ int tt=find(y); ans+=d[x]+d[y]-d[tt]*2; } } vis[x]=1; return; } int main(){ scanf("%d",&n); for(int i=1;i<n;i++){ scanf("%d%d",&a,&b); add(a,b); add(b,a); } scanf("%d",&m); for(int i=1;i<=n;i++)fa[i]=i; for(int i=1;i<=m;i++){ scanf("%d",&y); ask[x].push_back(y),ask[y].push_back(x); x=y; } d[1]=0; Tarjan(1,1); printf("%d",ans); return 0; }
bzoj1787 緊急集合
Description
Input
Output
Sample Input
6 41 2
2 3
2 4
4 5
5 6
4 5 6
6 3 1
2 4 4
6 6 6
Sample Output
5 2
2 5
4 1
6 0
HINT
處理樹後,枚舉每兩個節點求lca再讓第三個點連接的情況一共三種。感覺寫的有點麻煩了。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int d[500010],siz[500010],head[500010],fa[500010],son[500010],top[500010],cnt,n,m,u,v,a,b,c; struct edge{ int v,next; }E[1000010]; void add(int u,int v){ E[++cnt].v=v; E[cnt].next=head[u]; head[u]=cnt; } void dfs(int x,int dep){ siz[x]=1; d[x]=dep; for(int i=head[x];i;i=E[i].next){ int v=E[i].v; if(v==fa[x])continue; fa[v]=x; dfs(v,dep+1); siz[x]+=siz[v]; if(siz[v]>siz[son[x]])son[x]=v; } } void DFS(int x,int tp){ top[x]=tp; if(son[x])DFS(son[x],tp); for(int i=head[x];i;i=E[i].next){ int v=E[i].v; if(v==fa[x]||v==son[x])continue; DFS(v,v); } } int query(int x,int y){ while(top[x]!=top[y]){ if(d[top[x]]<d[top[y]])swap(x,y); x=fa[top[x]]; } return d[x]<d[y]?x:y; } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<n;i++){ scanf("%d%d",&u,&v); add(u,v);add(v,u); } dfs(1,1); DFS(1,1); for(int i=1;i<=m;i++){ scanf("%d%d%d",&a,&b,&c); int l1,l2,l3,t1,t2,t3,l,ans1,ans2,ans3,ans; l1=query(a,b);l2=query(b,c);l3=query(a,c); t1=query(l1,c);t2=query(l2,a);t3=query(l3,b); ans1=d[a]+d[b]-d[l1]*2; ans1+=d[l1]+d[c]-d[t1]*2; ans2=d[b]+d[c]-d[l2]*2; ans2+=d[l2]+d[a]-d[t2]*2; ans3=d[a]+d[c]-d[l3]*2; ans3+=d[l3]+d[b]-d[t3]*2; ans=min(ans1,min(ans2,ans3)); if(ans==ans1)l=l1; else if(ans==ans2)l=l2; else l=l3; printf("%d %d\n",l,ans); } }
還有一個樹剖練習:codevs4633[Mz]樹鏈剖分練習
時間限制: 1 s 空間限制: 64000 KB 題目描述 Description
給定一棵結點數為n的樹,初始點權均為0,有依次q個操作,每次操作有三個參數a,b,c,當a=1時,表示給b號結點到c號結點路徑上的所有點(包括b,c,下同)權值都增加1,當a=2時,表示詢問b號結點到c號結點路徑上的所有點權值之和。
輸入描述 Input Description第一行,一個正整數n。
接下來n-1行,每行一對正整數x,y,表示x號結點和y號結點之間有一條邊。
第n+1行,一個正整數q。
最後q行,每行一組正整數a,b,c,表示操作的三個參數。b和c可能相等。
保證數據都是合法的。
輸出描述 Output Description若幹行,每行一個非負整數表示答案。
樣例輸入 Sample Input5
1 2
2 3
1 4
2 5
5
1 4 5
2 1 5
1 1 3
2 5 3
2 4 3
樣例輸出 Sample Output
3
4
6
數據範圍及提示 Data Size & Hint
共有10個測試點,對於第i個測試點,當1<=i<=4時,n=q=10^i,當5<=i<=10時,n=q=10000*i。
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; #define maxn 100010 #define lson o<<1,l,m #define rson o<<1|1,m+1,r int siz[maxn],tree[maxn],deep[maxn],head[maxn],fa[maxn],son[maxn]; int sum[maxn<<2],add[maxn<<2]; int n,ord,top[maxn],ecnt; struct edge{ int u,v,next; }E[maxn<<1]; void added(int u,int v) { E[++ecnt].u=u; E[ecnt].v=v; E[ecnt].next=head[u]; head[u]=ecnt; } void dfs(int x) { siz[x]=1; for(int i=head[x] ; i ; i=E[i].next ) { int v=E[i].v; if(fa[x]==v)continue; deep[v]=deep[x]+1; fa[v]=x; dfs(v); siz[x]+=siz[v]; if(siz[son[x]]<siz[v])son[x]=v; } } void dfs2(int x,int tp) { tree[x]=++ord;top[x]=tp; if(son[x])dfs2(son[x],tp); for(int i=head[x] ; i ; i=E[i].next ) { int v=E[i].v; if(fa[x]==v||son[x]==v)continue; dfs2(v,v); } } void pushup(int o){sum[o]=sum[o<<1]+sum[o<<1|1];} void pushdown(int o,int x) { if(add[o]) { add[o<<1]+=add[o];add[o<<1|1]+=add[o]; sum[o<<1]+=add[o]*(x-(x>>1));sum[o<<1|1]+=add[o]*(x>>1); add[o]=0; } } void update(int o,int l,int r,int ql,int qr) { if(ql<=l&&r<=qr) { sum[o]+=r-l+1; add[o]++; return ; } int m=(l+r)>>1; pushdown(o,r-l+1); if(ql<=m)update(lson,ql,qr); if(qr>m)update(rson,ql,qr); pushup(o); return ; } /* int lca(int x,int y) { while(top[x]!=top[y]) { if(deep[top[x]]<deep[top[y]])swap(x,y); x=fa[top[x]]; } return deep[x]<deep[y]?x:y; } */ void do_add(int x,int y) { int fx=top[x],fy=top[y]; while(fx!=fy) { if(deep[fx]<deep[fy]){swap(fx,fy);swap(x,y);} update(1,1,n,tree[fx],tree[x]); x=fa[fx];fx=top[x]; } if(deep[x]>deep[y])swap(x,y); update(1,1,n,tree[x],tree[y]); return ; } int query(int o,int l,int r,int ql,int qr) { if(ql<=l&&r<=qr)return sum[o]; pushdown(o,r-l+1); int m=(l+r)>>1; int ret(0); if(ql<=m)ret+=query(lson,ql,qr); if(qr>m)ret+=query(rson,ql,qr); return ret; } int que(int x,int y) { int fx=top[x],fy=top[y]; int ret(0); while(fx!=fy) { if(deep[fx]<deep[fy]){swap(fx,fy);swap(x,y);} ret+=query(1,1,n,tree[fx],tree[x]); x=fa[fx];fx=top[x]; } if(deep[x]>deep[y])swap(x,y); ret+=query(1,1,n,tree[x],tree[y]); return ret; } int main() { int a,b,c,q; scanf("%d",&n); for(int i=1 ; i<n ; ++i ) { scanf("%d%d",&a,&b); added(a,b);added(b,a); } dfs(1);dfs2(1,1); scanf("%d",&q); while(q--) { scanf("%d%d%d",&a,&b,&c); if(a==1)do_add(b,c); else printf("%d\n",que(b,c)); } return 0; }
也是個板子題。
以及一道匈牙利
codevs2776 尋找代表元
時間限制: 1 s 空間限制: 256000 KB 題目描述 Description
廣州二中蘇元實驗學校一共有n個社團,分別用1到n編號。
廣州二中蘇元實驗學校一共有m個人,分別用1到m編號。每個人可以參加一個或多個社團,也可以不參加任何社團。
每個社團都需要選一個代表。謙哥希望更多的人能夠成為代表。
第一行輸入兩個數n和m。
以下n行每行若幹個數,這些數都是不超過m的正整數。其中第i行的數表示社團i的全部成員。每行用一個0結束。
輸出最多的能夠成為代表的人數。
樣例輸入 Sample Input4 4
1 2 0
1 2 0
1 2 0
1 2 3 4 0
3
數據範圍及提示 Data Size & Hint各個測試點1s
數據範圍
n,m<=200
把人和社團分開編號在建邊就行。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define N 203 int n,m,x,cnt=0; int head[2*N],ex[2*N]; bool vis[2*N]; struct edge{ int v,next; }E[100010]; void add(int u,int v){ E[++cnt].v=v; E[cnt].next=head[u]; head[u]=cnt; } bool find(int x){ vis[x]=1; for(int i=head[x];i;i=E[i].next){ int v=E[i].v; if(!vis[v]){ vis[v]=1; if(!ex[v]||find(ex[v])){ ex[v]=x; ex[x]=v; return true; } } } return false; } int match(){ int ans=0; for(int i=1;i<=n;i++){ memset(vis,0,sizeof(vis)); if(find(i))ans++; } return ans; } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++){ while(1){ scanf("%d",&x); if(x){ add(i,x+200);//分開編號 add(x+200,i); } else break; } } printf("%d\n",match()); return 0; }
復習模板有希望啊~
2017.10.24