bzoj3331: [BeiJing2013]压力
如今,路由器和交换机构建起了互联网的骨架。处在互联网的骨干位置的核心路由器典型的要处理100Gbit/s的网络流量。他们每天都生活在巨大的压力之下。
小强建立了一个模型。这世界上有N个网络设备,他们之间有M个双向的链接。这个世界是连通的。在一段时间里,有Q个数据包要从一个网络设备发送到另一个网络设备。
一个网络设备承受的压力有多大呢?很显然,这取决于Q个数据包各自走的路径。不过,某些数据包无论走什么路径都不可避免的要通过某些网络设备。
你要计算:对每个网络设备,必须通过(包括起点、终点)他的数据包有多少个?
题解:比较裸的双联通分量题目;
首先tarjan求双连通分量,将原图变成一棵树,那么题目要求的就是每个点被多少路径经过,求lca,然后做一个树上差分,判断一下这个点是否是原图的割点,若是,加上经过这一点的路径数;
不要忘了特判一下每个点起点和终点;
树链剖分求lca应该更快;
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<string> #include<ctime> #include<cmath> #include<set> #include<map> #include<queue> #include<algorithm> #include<iomanip> #include<stack> using namespace std; #define FILE "dealing" #define up(i,j,n) for(int i=(j);i<=(n);i++) #define pii pair<int,int> #define LL int #define mem(f,g) memset(f,g,sizeof(f)) namespace IO{ char buf[1<<15],*fs,*ft; int gc(){return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?-1:*fs++;} int read(){ int ch=gc(),f=0,x=0; while(ch<'0'||ch>'9'){if(ch=='-')f=1;ch=gc();} while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=gc();} return f?-x:x; } int readint(){ int ch=getchar(),f=0,x=0; while(ch<'0'||ch>'9'){if(ch=='-')f=1;ch=getchar();} while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();} return f?-x:x; } }using namespace IO; const int maxn=201000,inf=100000000; int n,m,Q; struct node{ int y,next; }e[maxn],E[maxn]; int linkk[maxn],len=0,linker[maxn],Len=0; inline void insert(int x,int y,int* linkk,int &len,node* e){e[++len].y=y;e[len].next=linkk[x];linkk[x]=len;} int x,y,ans[maxn],N=0,last=0,id[maxn],re[maxn];//记录每个点在新图中的编号; int u[maxn],v[maxn]; namespace maketree{ vector<int> bcc[maxn]; int pre[maxn],low[maxn],iscut[maxn],b[maxn],dfs_clock=0,cnt=0; int u[maxn],v[maxn],top=0; void tarjan(int x,int fa){ pre[x]=low[x]=++dfs_clock;int child=0; for(int i=linkk[x];i;i=e[i].next){ if(e[i].y==fa)continue; if(!pre[e[i].y]){ u[++top]=x,v[top]=e[i].y; tarjan(e[i].y,x); if(low[e[i].y]<low[x])low[x]=low[e[i].y]; if(low[e[i].y]>=pre[x]){ iscut[x]=1;cnt++;bcc[cnt].clear(); while(true){ if(b[u[top]]!=cnt){bcc[cnt].push_back(u[top]);b[u[top]]=cnt;} if(b[v[top]]!=cnt){bcc[cnt].push_back(v[top]);b[v[top]]=cnt;} if(u[top]==x&&v[top]==e[i].y){top--;break;} top--; } } } else if(pre[e[i].y]<pre[x]){ u[++top]=x,v[top]=e[i].y; if(pre[e[i].y]<low[x])low[x]=pre[e[i].y]; } } if(!fa&&child==1)iscut[x]=0; } void slove(){ tarjan(1,0); last=N=cnt; int x; up(i,1,cnt){ for(int j=0;j<bcc[i].size();j++){ x=bcc[i][j]; if(iscut[x]){ if(!id[x])id[x]=++N,re[id[x]]=x;//缩点后的点对应的实际的点 insert(id[x],i,linker,Len,E); insert(i,id[x],linker,Len,E); } else id[x]=i; } } } }; int num[maxn]; namespace st{ int fa[maxn][31],dep[maxn]; void dfs(int x,int f){ for(int i=linker[x];i;i=E[i].next){ if(E[i].y==f)continue; fa[E[i].y][0]=x; dep[E[i].y]=dep[x]+1; dfs(E[i].y,x); } } void getlca(int x,int y){ if(dep[y]<dep[x])swap(x,y); int u=x,v=y; for(int i=30;i>=0;i--) if(dep[y]-dep[x]>=(1<<i))y=fa[y][i]; if(x==y){ num[fa[x][0]]-=1; num[v]+=1; return; } for(int i=30;i>=0;i--)if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i]; num[fa[fa[x][0]][0]]-=1; num[fa[x][0]]-=1; num[u]+=1;num[v]+=1; } void slove(){ dfs(1,0); up(j,1,30)up(i,1,N)fa[i][j]=fa[fa[i][j-1]][j-1]; int x,y; up(i,1,Q){ u[i]=x=read(),v[i]=y=read(); x=id[x];y=id[y]; if(x!=y)getlca(x,y); } } }; int dfs(int x,int fa){ int sum=num[x]; for(int i=linker[x];i;i=E[i].next){ if(E[i].y==fa)continue; sum+=dfs(E[i].y,x); } if(x>last){ ans[re[x]]+=sum; } return sum; } int main(){ n=read(),m=read(),Q=read(); up(i,1,m){ x=read(),y=read(); insert(x,y,linkk,len,e); insert(y,x,linkk,len,e); } maketree::slove(); st::slove(); up(i,1,Q){ if(id[u[i]]<=last)ans[u[i]]++; if(id[v[i]]<=last)ans[v[i]]++; } dfs(1,0); up(i,1,n)printf("%d%c",ans[i],i==n?'\n':' '); return 0; }