受欢迎的牛(Bzoj1051)
这道题题意是找出除自己之外的所有牛认为是受欢迎的牛的数量
这道题就是找到个点使得除了它自己别的点都能到达它,求这种点的个数
显然这张图不能成为一个环,所以我们先缩点。
在这张缩完点的图中我们找出一个出度为0的点即可,这个点所包含的原点数(因为这个点是缩完的点)即为答案
#include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #include<cmath> #include<queue> #define ll long long using namespace std; const int maxn=1000000+10101; inline int read(){ int x=0,f=1;char ch=getchar(); for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1; for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-'0'; return x*f; } int n,m; int tot,head[maxn],to[maxn],nx[maxn]; inline void add(int x,int y){ to[++tot]=y;nx[tot]=head[x];head[x]=tot; return ; } int book[maxn],cnt,co[maxn],dfn[maxn],low[maxn],tt,top,st[maxn]; void tarjan(int x){ low[x]=dfn[x]=++tt; st[++top]=x; for(int i=head[x];i;i=nx[i]){ int k=to[i]; if(!dfn[k]){ tarjan(k); low[x]=min(low[x],low[k]); } else if(!book[k])low[x]=min(low[x],dfn[k]); } if(dfn[x]==low[x]){ book[x]=++cnt; co[cnt]++; while(st[top]!=x){ book[st[top]]=cnt; co[cnt]++; top--; } top--; } return ; } int out[maxn]; int main(){ n=read();m=read(); for(int i=1;i<=m;i++){ int x,y; x=read();y=read(); add(x,y); } for(int i=1;i<=n;i++){ if(!dfn[i])tarjan(i); } for(int i=1;i<=n;i++){ for(int j=head[i];j;j=nx[j]){ int k=book[to[j]]; if(k!=book[i])out[book[i]]++; } } int ans=0,u=0; for(int i=1;i<=cnt;i++){ if(!out[i])ans=co[i],u++; } if(u!=1)u=0; printf("%d",ans*u); return 0; }
最大半连通子图(Bzoj1093)
这道题就是找出半联通子图内点的个数最大值和最大半联通子图个数
半联通就是一个图内任意两点u,v ,u可以到v或者v可以到u
先将原图缩点,重构图,进行拓扑排序找最长链
#include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #include<cmath> #include<queue> #define ll long long using namespace std; const int maxn=3000000+10101; const int MAXN=100000+101010; inline int read(){ int x=0,f=1;char ch=getchar(); for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1; for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-'0'; return x*f; } inline void write(int x){ int len=1,t=10; while(t<=x)len++,t*=10; while(len--)t/=10,putchar(x/t+48),x%=t; return ; } int n,m,MOD; int tot,head[maxn<<1],to[maxn<<1],nx[maxn<<1]; inline void add(int x,int y){ to[++tot]=y;nx[tot]=head[x];head[x]=tot; return ; } int cnt,dfn[MAXN],low[MAXN],tt,top,st[maxn],co[MAXN],num[maxn]; void tarjan(int x){ dfn[x]=low[x]=++tt; st[++top]=x; for(int i=head[x];i;i=nx[i]){ int k=to[i]; if(!dfn[k]){ tarjan(k); low[x]=min(low[x],low[k]); } else if(!co[k])low[x]=min(low[x],dfn[k]); } if(low[x]==dfn[x]){ co[x]=++cnt; num[cnt]++; while(st[top]!=x){ co[st[top]]=cnt; num[cnt]++; top--; } top--; } return ; } int book[maxn],vis[maxn],in[maxn],dp[maxn];//dp记录半联通子图内点的个数,book记录相同半联通子图内的点的个数相同的子图个数 void dfs(){ //拓扑排序找最长链 queue<int>q; for(int i=n+1;i<=n+cnt;i++){ if(in[i]==0){ q.push(i); dp[i]=num[i-n]; book[i]=1; vis[i]=1; } } while(!q.empty()){ int u=q.front(); q.pop(); for(int i=head[u];i;i=nx[i]){ int k=to[i]; in[k]--; if(in[k]==0)q.push(k); if(vis[k]==u)continue;//判重边 if(dp[k]<dp[u]+num[k-n]){ dp[k]=(dp[u]+num[k-n])%MOD; book[k]=book[u]%MOD; } else if(dp[k]==dp[u]+num[k-n]){ book[k]=(book[k]+book[u])%MOD; } vis[k]=u; } } return ; } int main(){ n=read();m=read();MOD=read(); for(int i=1;i<=m;i++){ int x,y; x=read();y=read(); add(x,y); } for(int i=1;i<=n;i++){ if(!dfn[i])tarjan(i); } for(int i=1;i<=n;i++){ for(int j=head[i];j;j=nx[j]){ int k=co[to[j]]+n; if(k!=co[i]+n){ add(co[i]+n,k); in[k]++; } } } dfs(); int ans=0,ann=0; for(int i=1+n;i<=cnt+n;i++){ if(dp[i]>ans){ans=dp[i]%MOD;ann=book[i]%MOD;} else if(dp[i]==ans){ ann=(ann+book[i])%MOD; ans=ans%MOD; } } write(ans); putchar('\n'); write(ann); return 0; }
网络协议
这道题题意是计算最少需要将一个新软件直接提供给多少个学校,才能使软件通过网络被传送到所有学校;添加几条边使得图成强联通
板子题
首先缩点,记录入度为0的点个数即为第一个答案
使一个DAG成一个强联通,最优的连边是出度为0的点连想入度为0的点,所以max(入度为0点的个数,出度为0点个数)
但要判断缩完点只剩一个点的情况,这种情况输出0
#include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #include<cmath> #include<queue> #define ll long long using namespace std; const int maxn=1000000+10101; inline int read(){ int x=0,f=1;char ch=getchar(); for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1; for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-'0'; return x*f; } int n; int tot,head[maxn],to[maxn],nx[maxn]; inline void add(int x,int y){ to[++tot]=y;nx[tot]=head[x];head[x]=tot; return ; } int dfn[maxn],low[maxn],co[maxn],st[maxn],cnt,tt,top; void tarjan(int x){ low[x]=dfn[x]=++tt; st[++top]=x; for(int i=head[x];i;i=nx[i]){ int k=to[i]; if(!dfn[k]){ tarjan(k); low[x]=min(low[x],low[k]); } else if(!co[k])low[x]=min(low[x],dfn[k]); } if(low[x]==dfn[x]){ co[x]=++cnt; while(st[top]!=x){ co[st[top]]=cnt; top--; } top--; } return ; } int in[maxn],out[maxn]; int main(){ n=read(); for(int i=1;i<=n;i++){ for(;;){ int x=read(); if(x==0)break; add(i,x); } } for(int i=1;i<=n;i++){ if(!dfn[i])tarjan(i); } for(int i=1;i<=n;i++){ for(int j=head[i];j;j=nx[j]){ int k=to[j]; if(co[k]!=co[i]){ in[co[k]]++; out[co[i]]++; } } } int ans=0,maxn=0; for(int i=1;i<=cnt;i++){ if(in[i]==0)ans++; if(out[i]==0)maxn++; } printf("%d\n",ans); if(cnt==1)maxn=0,ans=0; printf("%d",max(maxn,ans)); return 0; }