[NOIp复习计划]:图的连通
2017-07-19 22:49
176 查看
[bzoj 2427][HAOI2010]软件安装
考虑到有环,就tarjan一发,把价值和空间合并一下,然后会发现,缩点之后是个森林,就可以跑树形dp了
设f[i][j]表示第i个节点,空间为j的时候的最大价值,然后类似背包的方式dp一发就行了。
f[i][j]=f[to[i]][k]+f[i][j−k]
注意边界,然后就RE到死了 a了。
对了vector.size()返回的不是int,而是size_type,它被定义成unsigned,所以 < size(),改写成< =size()-1,就有可能出事。。。
[bzoj 2438][中山市选2011]杀人游戏
对于一个环来说,就询问一个人就够了,因为剩下的都能由这个人直接或间接的推出来。
啥都别说,先缩点,然后就变成了DAG,显然对于缩点后入度为0的点我们都要询问一次。
然而这并不对,这就是这题的坑了,对于一个独立或是所连的点的入度大于1的点,我们不需要对它询问,因为它可以由其他n-1个点推出来,但是一张图里最多就这一个点,特判一下就好了。
答案的计算就是最朴素的计算方式,需要询问的点/总点数
[bzoj 2730][HNOI2012]矿场搭建
破坏一个点,我们就考虑破坏割点的时候,如果一张图没有割点,我们就需要两个点,保证答案合法
而对于其他因为割点分成的连通块只需要选一个就好了,剩下的就是乘法原理乱搞。
[bzoj 3391][Usaco2004 Dec]Tree Cutting网络破坏
太水了,随便dfs一下就完了
懒癌发作,复制一篇0_0
[bzoj 2208] [Jsoi2010]连通数
有环很烦的,于是先缩点,然后重新建图,这个时候就是DAG了,于是我们就可以dp了,这里我用了bitset优化,我以为我这么卡常一定垫底了,结果我看看rk6一脸懵逼,然后我才发现他们都是传递闭包做的……
[bzoj 1123][POI2008]BLO
答案是有向的数对,以及算了毁掉的点,参见discuss
我们考虑有向图的dfs树是没有交叉边的(连接到别的树上的边)这样的话如果该点是割点,那么分割的点对,是可以用dfs求的,就是每个点维护一个size,而对于非割点的点,答案一定是一样的
[codevs 2822] 爱在心中
缩点之后是DAG各种O(n)的性质乱搞就行了,其实暴力也可以。
考虑到有环,就tarjan一发,把价值和空间合并一下,然后会发现,缩点之后是个森林,就可以跑树形dp了
设f[i][j]表示第i个节点,空间为j的时候的最大价值,然后类似背包的方式dp一发就行了。
f[i][j]=f[to[i]][k]+f[i][j−k]
注意边界,然后就RE到死了 a了。
对了vector.size()返回的不是int,而是size_type,它被定义成unsigned,所以 < size(),改写成< =size()-1,就有可能出事。。。
#include<bits/stdc++.h> using namespace std; const 12b10 int MAXN = 120; const int MAXM = 560; int f[MAXN][MAXM],n,m,w[MAXN],v[MAXN]; #define rep(i,a,b) for(i=a;i<=b;i++) #define dep(i,a,b) for(i=a;i>=b;i--) typedef vector<int> P; vector<P> mp; vector<P> mp2; int cnt,clo,dfn[MAXN],low[MAXN],belong[MAXN],top,sta[MAXN]; bool ins[MAXN]; int v2[MAXN],w2[MAXN],in[MAXN]; void dfs(int x){ int i; dfn[x]=low[x]=++clo; sta[++top]=x;ins[x]=1; rep(i,0,((int)mp[x].size()-1)){ if(!dfn[mp[x][i]]){ dfs(mp[x][i]); low[x]=min(low[x],low[mp[x][i]]); }else if(ins[mp[x][i]]){ low[x]=min(low[x],dfn[mp[x][i]]); } } if(dfn[x]==low[x]){ cnt++; while(top!=0&&sta[top]!=x){ belong[sta[top]]=cnt; ins[sta[top--]]=0; } belong[sta[top]]=cnt; ins[sta[top--]]=0; } } void dp(int x){ int i,j,k; int len=(int)mp2[x].size()-1; rep(i,0,len){ if(mp2[x][i]==x)continue; dp(mp2[x][i]); dep(j,m-w2[x],0){ rep(k,0,j){ f[x][j]=max(f[x][j],f[mp2[x][i]][k]+f[x][j-k]); } } } dep(i,m,0){ if(i>=w2[x]){ f[x][i]=f[x][i-w2[i]]+v2[x]; }else{ f[x][i]=0; } } } int main(){ int i,j; scanf("%d%d",&n,&m); mp=vector<P>(n+5); rep(i,1,n)scanf("%d",&w[i]); rep(i,1,n)scanf("%d",&v[i]); rep(i,1,n){ int a;scanf("%d",&a); mp[a].push_back(i); } rep(i,0,n)if(!dfn[i])dfs(i); mp2=vector<P>(cnt+5); rep(i,0,n){ rep(j,0,(int)mp[i].size()-1){ if(belong[i]==belong[mp[i][j]])continue; mp2[belong[i]].push_back(belong[mp[i][j]]); in[belong[mp[i][j]]]++; } v2[belong[i]]+=v[i]; w2[belong[i]]+=w[i]; } rep(i,1,cnt){if(!in[i]){mp2[0].push_back(i);}}; dp(0); printf("%d",f[0][m]); return 0; }
[bzoj 2438][中山市选2011]杀人游戏
对于一个环来说,就询问一个人就够了,因为剩下的都能由这个人直接或间接的推出来。
啥都别说,先缩点,然后就变成了DAG,显然对于缩点后入度为0的点我们都要询问一次。
然而这并不对,这就是这题的坑了,对于一个独立或是所连的点的入度大于1的点,我们不需要对它询问,因为它可以由其他n-1个点推出来,但是一张图里最多就这一个点,特判一下就好了。
答案的计算就是最朴素的计算方式,需要询问的点/总点数
#include<bits/stdc++.h> using namespace std; const int maxn = 100505; const int maxm = 500505; #define rep(i,a,b) for(i=a;i<=b;i++) #define dep(i,a,b) for(i=a;i>=b;i--) int h[maxn],to[maxm],nx[maxm],tot; int n,m; int dfn[maxn],low[maxn],ti,sta[maxn],cnt,top,size[maxn],in[maxn],belong[maxn]; void add_edge(int u,int v){ to[++tot]=v;nx[tot]=h[u];h[u]=tot; } bool ins[maxn]; void dfs(int x){ dfn[x]=low[x]=++ti; sta[++top]=x;ins[x]=1; for(int i=h[x];i;i=nx[i]){ if(!dfn[to[i]]){ dfs(to[i]); low[x]=min(low[x],low[to[i]]); }else if(ins[to[i]]){ low[x]=min(low[x],dfn[to[i]]); } } if(dfn[x]==low[x]){ int now=0;cnt++; while(x!=now){ now=sta[top];top--; belong[now]=cnt; ins[now]=0; size[belong[now]]++; } } } void build(){ int i,j; rep(i,1,n){ for(j=h[i];j;j=nx[j]){ if(belong[i]!=belong[to[j]]){ in[belong[to[j]]]++; } } } } int main(){ int i,a,b,ans=0; bool flag=0; scanf("%d%d",&n,&m); rep(i,1,m){ scanf("%d%d",&a,&b); add_edge(a,b); } rep(i,1,n)if(!dfn[i])dfs(i);; build(); rep(i,1,cnt)if(in[i]==0)ans++; rep(i,1,n){ if(size[belong[i]]==1&&in[belong[i]]==0){ bool flag2=1; for(int j=h[i];j;j=nx[j]){ if(in[belong[to[j]]]==1){ flag2=0; break; } } if(flag2)flag=1; } } if(flag)ans--; printf("%.6lf",(double)(n-ans)/(double)n); return 0; }
[bzoj 2730][HNOI2012]矿场搭建
破坏一个点,我们就考虑破坏割点的时候,如果一张图没有割点,我们就需要两个点,保证答案合法
而对于其他因为割点分成的连通块只需要选一个就好了,剩下的就是乘法原理乱搞。
#include<iostream> #include<cstdio> #include<cstring> #define N 10005 using namespace std; int ri,n,m,cnt,now,all,tot,dfsclk,fst ,pnt ,nxt ,pos ,low ,mrk ,ans1; bool cut ; long long ans2; void add(int x,int y){ pnt[++tot]=y; nxt[tot]=fst[x]; fst[x]=tot; } void dfs(int x,int fa){ int p,sz=0; pos[x]=low[x]=++dfsclk; for (p=fst[x]; p; p=nxt[p]){ int y=pnt[p]; if (y==fa) continue; if (!pos[y]){ sz++; dfs(y,x); } low[x]=min(low[x],low[y]); if (low[y]>=pos[x] && fa) cut[x]=1; } if (!fa && sz>1) cut[x]=1; if (cut[x]) cnt++; } void calc(int x){ mrk[x]=now; all++; int p; for (p=fst[x]; p; p=nxt[p]){ int y=pnt[p]; if (cut[y] && mrk[y]!=now){ mrk[y]=now; cnt++; } if (!mrk[y]) calc(y); } } int main(){ while (~scanf("%d",&m) && m){ int i,x,y; tot=n=0; memset(fst,0,sizeof(fst)); for (i=1; i<=m; i++){ scanf("%d%d",&x,&y); n=max(n,max(x,y)); add(x,y); add(y,x); } memset(pos,0,sizeof(pos)); memset(low,0,sizeof(low)); dfsclk=cnt=0; memset(cut,0,sizeof(cut)); for (i=1; i<=n; i++) if (!pos[i]) dfs(i,0); if (!cnt){ ans1=2; ans2=(long long)n*(n-1)>>1; } else{ ans1=0; ans2=1; memset(mrk,0,sizeof(mrk)); for (i=1; i<=n; i++) if (!cut[i] && !mrk[i]){ cnt=all=0; now++; calc(i); if (cnt==1){ ans1++; ans2*=all; } } } printf("Case %d: %d %lld\n",++ri,ans1,ans2); } }
[bzoj 3391][Usaco2004 Dec]Tree Cutting网络破坏
太水了,随便dfs一下就完了
懒癌发作,复制一篇0_0
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #include<cmath> #include<queue> #define ll long long #define N 10005 using namespace std; int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } struct Node{ int to,next; }e[2*N]; int head ,n,num ,tot=0; bool vis ; void add(int u,int v) { e[++tot]=(Node){v,head[u]};head[u]=tot; } void dfs(int rt) { num[rt]=1;vis[rt]=1; for(int i=head[rt];i;i=e[i].next) { if(!vis[e[i].to]) { dfs(e[i].to); num[rt]+=num[e[i].to]; } else e[i].to=-1; } } int main() { n=read();int u,v; for(int i=1;i<n;i++) { u=read();v=read(); add(u,v);add(v,u); } dfs(1); double mid=n/2; for(int i=1;i<=n;i++) { bool ok=true; for(int j=head[i];j;j=e[j].next) if(e[j].to!=-1&&num[e[j].to]>mid){ok=false;break;} if(n-num[i]>mid)ok=false; if(ok==false)continue; printf("%d\n",i); } return 0; }
[bzoj 2208] [Jsoi2010]连通数
有环很烦的,于是先缩点,然后重新建图,这个时候就是DAG了,于是我们就可以dp了,这里我用了bitset优化,我以为我这么卡常一定垫底了,结果我看看rk6一脸懵逼,然后我才发现他们都是传递闭包做的……
#include<bits/stdc++.h> using namespace std; const int maxn = 2300; int n,h[maxn],to[maxn*maxn],nx[maxn*maxn],tot; bitset<2005>vis[maxn]; int bl[maxn],cnt; int h2[maxn],to2[maxn*maxn],nx2[maxn*maxn],tot2; char str[3000]; int dfn[maxn],low[maxn],sta[maxn],ti,top; bool ins[maxn]; bool has_solved[maxn]; void add_edge(int u,int v){ to[++tot]=v;nx[tot]=h[u];h[u]=tot; } void add_edge2(int u,int v){ to2[++tot2]=v;nx2[tot2]=h2[u];h2[u]=tot2; } void dfs(int x){ dfn[x]=low[x]=++ti; sta[++top]=x;ins[x]=1; for(int i=h[x];i;i=nx[i]){ if(!dfn[to[i]]){ dfs(to[i]); low[x]=min(low[x],low[to[i]]); }else if(ins[to[i]]){ low[x]=min(low[x],dfn[to[i]]); } } if(dfn[x]==low[x]){ int now=0;cnt++; while(now!=x){ now=sta[top--]; bl[now]=cnt; ins[now]=0; vis[cnt][now]=1; } } } void build(){ for(int i=1;i<=n;i++){ for(int j=h[i];j;j=nx[j]){ if(bl[i]!=bl[to[j]]){ add_edge2(bl[i],bl[to[j]]); } } } } void dp(int x){ if(has_solved[x]==1)return; has_solved[x]=1; for(int i=h2[x];i;i=nx2[i]){ dp(to2[i]); vis[x] |= vis[to2[i]]; } } int getans(){ int ans=0; for(int i=1;i<=n;i++){ ans += vis[bl[i]].count(); } return ans; } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%s",str+1); int j=1; while(str[j]!=0){ if(str[j]=='1'){ add_edge(i,j); } j++; } } for(int i=1;i<=n;i++)if(!dfn[i])dfs(i); build(); for(int i=1;i<=cnt;i++)if(!has_solved[i])dp(i); printf("%d",getans()); return 0; }
[bzoj 1123][POI2008]BLO
答案是有向的数对,以及算了毁掉的点,参见discuss
我们考虑有向图的dfs树是没有交叉边的(连接到别的树上的边)这样的话如果该点是割点,那么分割的点对,是可以用dfs求的,就是每个点维护一个size,而对于非割点的点,答案一定是一样的
#include<bits/stdc++.h> using namespace std; const int maxn = 100050; const int maxm = 500050; int n,m; int h[maxn],to[maxm<<1],nx[maxm<<1],tot; int low[maxn],dfn[maxn],ti; int sz[maxn]; bool vis[maxn]; typedef long long ll; ll ans[maxn]; void ins(int u,int v){ to[++tot]=v;nx[tot]=h[u];h[u]=tot; to[++tot]=u;nx[tot]=h[v];h[v]=tot; } void dfs(int x){ ll sum=0; vis[x]=1;sz[x]=1; low[x]=dfn[x]=++ti; for(int i=h[x];i;i=nx[i]){ if(!dfn[to[i]]){ dfs(to[i]); sz[x]+=sz[to[i]]; low[x]=min(low[x],low[to[i]]); if(dfn[x]<=low[to[i]]){ ans[x]+=sum*sz[to[i]]; sum+=sz[to[i]]; } }else if(vis[to[i]]){ low[x]=min(low[x],dfn[to[i]]); } } ans[x]+=sum*(n-sum-1); } int main(){ scanf("%d%d",&n,&m); int a,b; for(int i=1;i<=m;i++){ scanf("%d%d",&a,&b); ins(a,b); } dfs(1); for(int i=1;i<=n;i++) printf("%lld\n",(ans[i]+n-1)*2); return 0; }
[codevs 2822] 爱在心中
缩点之后是DAG各种O(n)的性质乱搞就行了,其实暴力也可以。
#include<bits/stdc++.h> using namespace std; const int maxn = 2000; int n,m,mp[maxn][maxn]; int mp2[maxn][maxn],bl[maxn],cnt,ti; int low[maxn],dfn[maxn]; int pre[maxn]; int sta[maxn],top,sz[maxn]; bool ins[maxn]; void dfs(int x){ sta[++top]=x; ins[x]=1; low[x]=dfn[x]=++ti; for(int i=1;i<=n;i++){ if(!mp[x][i])continue; if(!dfn[i]){ dfs(i); low[x]=min(low[x],low[i]); }else if(ins[i]){ low[x]=min(low[x],dfn[i]); } } if(dfn[x]==low[x]){ int now=0;cnt++; while(now!=x){ now=sta[top--];ins[now]=0; bl[now]=cnt;sz[cnt]++; } } } int in[maxn]; void build(){ for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(mp[i][j] && bl[i]!=bl[j]) mp2[bl[i]][bl[j]]=1,in[bl[j]]++; } void solve(int x,int va){ for(int i=1;i<=cnt;i++){ if(mp2[x][i]){ sz[i]+=va; solve(i,va); } } } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=m;i++){ int a,b; scanf("%d%d",&a,&b); mp[a][b]=1; } for(int i=1;i<=n;i++)if(!dfn[i])dfs(i); build(); int ans=0; for(int i=1;i<=cnt;i++)pre[i]=sz[i]; for(int i=1;i<=cnt;i++)if(sz[i]>1)ans++; printf("%d\n",ans); for(int i=1;i<=cnt;i++)solve(i,pre[i]); int id=0; for(int i=1;i<=cnt;i++)if(sz[i]>=n && pre[i]>1)id=i; for(int i=1;i<=n;i++) if(bl[i]==id){ printf("%d ",i); } if(id==0)printf("-1"); }
相关文章推荐
- [NOIp复习计划]:并查集
- NOIP复习计划
- [NOIp复习计划]:模拟
- [ 冲刺NOIP2016 ] 复习计划
- [置顶] NOIP 2017 复习计划
- [NOIp复习计划]:贪心
- [NOIp复习计划]:二分答案
- [NOIp复习计划]:构造
- [NOIp复习计划]:差分约束
- NOIP复习计划
- NOIP2015 运输计划(bzoj4326)
- [BZOJ]4326: NOIP2015 运输计划 二分+树链剖分+差分
- 模板复习计划
- 津津的储蓄计划 2004年NOIP全国联赛提高组
- [NHZXOI2017]【2016NOIP】堆复习
- OI路上-NOIP100天冲刺计划
- NOIP2015运输计划题解报告
- 数据结构复习计划(考研)
- POJ-2421 最小生成树 算法复习计划
- [NOIP复习]第三章:动态规划