11.01
有两道题调了很久(都差不多有一两个小时)都没调出来,明天(10.30)准备和题解对拍了……
图论:割点、桥、点双、边双、二分图
一些 NOI Online
POJ1144 网络
题意:
求无向图割点数量。 \((1\le N<100)\)
代码:
#include <cstdio>
#include <vector>
#include <algorithm>
#define y e[x][i]
using namespace std;
const int N=105;
int n,s,dfn[N],low[N];
bool cut[N];
vector<int> e[N];
void tarjan(int x,int rt){
int&l=low[x],cnt=0;dfn[x]=l=++s;
for(int i=0;i<e[x].size();i++)
if(!dfn[y]){
tarjan(y,rt);cnt++;l=min(l,low[y]);
if(low[y]>=dfn[x]) cut[x]=true;
}
else l=min(l,dfn[y]);
if(x==rt&&cnt==1) cut[x]=false;
}
void solve(){
s=0;
for(int i=1;i<=n;i++) {e[i].clear();dfn[i]=low[i]=cut[i]=0;}
for(int u,v;~scanf("%d",&u)&&u;)
while(getchar()!='\n') {scanf("%d",&v);e[u].push_back(v);e[v].push_back(u);}
for(int i=1;i<=n;i++)
if(!dfn[i]) tarjan(i,i);
printf("%d\n",count(cut+1,cut+n+1,true));
}
signed main(){
while(~scanf("%d",&n)&&n) solve();
return 0;
}
POJ2117 Electricity
题意:
求一个 \(N\) 个点 \(M\) 条边的无向图图删除一个点之后,最多有多少个连通分量。 \((1\le N\le10^4)\)
代码:
#include <cstdio>
#include <vector>
#include <algorithm>
#define y e[x][i]
using namespace std;
const int N=10005;
int n,m,s,cnt,dfn[N],low[N],cut[N];
vector<int> e[N];
void tarjan(int x,int rt){
int&l=low[x];dfn[x]=l=++s;
for(int i=0;i<e[x].size();i++)
if(!dfn[y]){
tarjan(y,rt);l=min(l,low[y]);
if(low[y]>=dfn[x]) cut[x]++;
}
else l=min(l,dfn[y]);
if(x==rt) cut[x]--;
}
void solve(){
cnt=s=0;
for(int i=0;i<n;i++) {e[i].clear();dfn[i]=low[i]=cut[i]=0;}
while(m--) {int u,v;scanf("%d%d",&u,&v);e[u].push_back(v);e[v].push_back(u);}
for(int i=0;i<n;i++)
if(!dfn[i]) {cnt++;tarjan(i,i);}
printf("%d\n",cnt+*max_element(cut,cut+n));
}
signed main(){
while(~scanf("%d%d",&n,&m)&&n) solve();
return 0;
}
HDU4587 TWO NODES
题意:
求无向图中删除任意两个点之后所能获得的独立连通分量个数的最大值。 \((3\le N,M\le5000)\)
代码:
#include <cstdio>
#include <vector>
#include <algorithm>
#define y e[x][i]
using namespace std;
const int N=5005;
int n,m,s,d,cnt,ans,dfn[N],low[N],cut[N];
vector<int> e[N];
void tarjan(int x,int rt){
int&l=low[x];dfn[x]=l=++s;
for(int i=0;i<e[x].size();i++)
if(y!=d){
if(!dfn[y]){
tarjan(y,rt);l=min(l,low[y]);
if(low[y]>=dfn[x]) cut[x]++;
}
else l=min(l,dfn[y]);
}
if(x==rt) cut[x]--;
}
void solve(){
ans=0;
for(int i=0;i<n;i++) e[i].clear();
while(m--) {int u,v;scanf("%d%d",&u,&v);e[u].push_back(v);e[v].push_back(u);}
for(d=0;d<n;d++){
fill_n(dfn,n,0);fill_n(cut,n,0);cut[d]=-1;cnt=s=0;
for(int i=0;i<n;i++)
if(i!=d&&!dfn[i]) {cnt++;tarjan(i,i);}
ans=max(ans,cnt+*max_element(cut,cut+n));
}
printf("%d\n",ans);
}
signed main(){
while(~scanf("%d%d",&n,&m)&&n) solve();
return 0;
}
HDU3749 Financial Crisis
题意:
求无向图两点之间是否有边,有一条边,或大于等于两条。 \((1\le N\le5000)\)
代码:
#include <cstdio>
#include <vector>
#include <algorithm>
#define y e[x][i]
using namespace std;
const int N=5005;
int t,n,m,q,top,sum,cnt,s[N],f[N],dfn[N],low[N];
vector<int> e[N],b[N];
int getf(int x) {return f[x]==x?x:f[x]=getf(f[x]);}
void tarjan(int x){
int&l=low[x];dfn[x]=l=++sum;s[++top]=x;
for(int i=0;i<e[x].size();i++)
if(!dfn[y]){
tarjan(y);l=min(l,low[y]);
if(low[y]>=dfn[x]){
if(s[top]==y) {top--;continue;}
cnt++;b[x].push_back(cnt);
while(s[top+1]!=y) b[s[top--]].push_back(cnt);
}
}
else l=min(l,dfn[y]);
}
void chk(int u,int v){
if(getf(u)!=getf(v)) {puts("zero");return;}
for(int i=0;i<b[u].size();i++)
for(int j=0;j<b[v].size();j++)
if(b[u][i]==b[v][j]) {puts("two or more");return;}
puts("one");
}
void solve(){
top=sum=cnt=0;
for(int i=0;i<n;i++) {e[i].clear();b[i].clear();dfn[i]=low[i]=0;f[i]=i;}
while(m--) {int u,v;scanf("%d%d",&u,&v);e[u].push_back(v);e[v].push_back(u);f[getf(u)]=f[getf(v)];}
for(int i=0;i<n;i++)
if(!dfn[i]) tarjan(i);
printf("Case %d:\n",++t);
while(q--) {int u,v;scanf("%d%d",&u,&v);chk(u,v);}
}
signed main(){
while(~scanf("%d%d%d",&n,&m,&q)&&n) solve();
return 0;
}
好玄学哦,之前写了一个时间复杂度严格 \(N^2\) 的结果 T 了,换成这个时间复杂度有点问题的写法才过。
POJ3177 分离的路径
题意:
求无向图最少加几条边使其成为边双。 \((1\le N\le5000,1\le M\le10^4)\)
代码:
#include <bits/stdc++.h>
#define id e[x][i]
#define y v[id]
using namespace std;
const int N=5005,M=20005;
int n,m,s1,s2,top,ans,s[N],d[N],dfn[N],low[N],bel[N],v[M];
bool vis[M];
vector<int> e[N];
void tarjan(int x){
int&l=low[x];l=dfn[x]=++s1;s[++top]=x;
for(int i=0;i<e[x].size();i++)
if(!vis[id]){
vis[id^1]=true;
if(!dfn[y]) {tarjan(y);l=min(l,low[y]);}
else l=min(l,dfn[y]);
}
if(l!=dfn[x]) return;bel[x]=++s2;
for(int i;i!=x;) bel[i=s[top--]]=s2;
}
signed main(){
scanf("%d%d",&n,&m);
for(int i=0,p,q;i<m;i++) {p=i<<1;q=i<<1|1;scanf("%d%d",&v[q],&v[p]);e[v[q]].push_back(p);e[v[p]].push_back(q);}
for(int i=1;i<=n;i++)
if(!dfn[i]) tarjan(i);
for(int x=1;x<=n;x++)
for(int i=0;i<e[x].size();i++)
if(bel[x]!=bel[y]) {d[bel[x]]++;d[bel[y]]++;}
for(int i=1;i<=s2;i++) ans+=d[i]==2;
printf("%d\n",(ans+1)/2);
return 0;
}
POJ3352 Road Construction
除了无重边之类的毒瘤东西和上面一样,不过可以用 low 判,尽管这样求边双是假的但是求叶子节点并没什么问题。
LA5135 井下矿工
题意:
给一张联通无向图选定最少的点使删除任意点后其余点都可以和至少一个被选点联通。 \((1\le N\le50000)\) ,答案在 long long 范围内。
代码:
啊啊啊啊啊啊啊啊啊怎么都没调出来, udebug 发现垃圾数据直接给你一个样例。求 debug 。
#include <bits/stdc++.h>
#define y e[x][i]
using namespace std;
typedef long long ll;
const int N=1e5+5;
int t,n,m,sum,top,ans1,s[N],dfn[N],low[N],cut[N];
ll ans2;
vector<int> e[N];
vector<vector<int> > b;
void tarjan(int x){
int&l=low[x],ch=0;l=dfn[x]=++sum;s[++top]=x;
for(int i=0;i<e[x].size();i++)
if(!dfn[y]){
tarjan(y);l=min(l,low[y]);ch++;
if(low[y]>=dfn[x]){
vector<int> bcc;bcc.push_back(x);cut[x]=1;
while(s[top+1]!=y) bcc.push_back(s[top--]);
b.push_back(bcc);
}
}
else l=min(l,dfn[y]);
if(x==1&&ch==1) cut[x]=0;
}
void solve(){
b.clear();ans1=sum=top=0;ans2=1;
for(int i=1;i<=m*2;i++) {e[i].clear();s[i]=dfn[i]=cut[i]=0;}
while(m--) {int u,v;scanf("%d%d",&u,&v);e[u].push_back(v);e[v].push_back(u);}
tarjan(1);
if(b.size()==1) {printf("Case %d: 2 %lld\n",++t,1ll*b[0].size()*(b[0].size()-1)/2);return;}
for(int i=0;i<b.size();i++){
int cnt=0;
for(int j=0;j<b[i].size();j++) cnt+=cut[b[i][j]];
if(cnt==1) {ans1++;ans2*=b[i].size()-1ll;}
}
printf("Case %d: %d %lld\n",++t,ans1,ans2);
}
signed main(){
while(~scanf("%d",&m)&&m) solve();
return 0;
}
upd: 过了,点数可以是边数的两倍
HDU3394 Railway
题意:
求无向图桥的个数和在多于一个环中的边的个数。 \((1\le N\le10^4,1\le M\le10^5)\)
代码:
静态查错不出来,讨论里的几个数据也没用……求 debug 。
#include <bits/stdc++.h>
#define y e[x][i]
using namespace std;
const int N=10005;
int n,m,sum,top,ans1,ans2,s[N],dfn[N],low[N];
bool now[N];
vector<int> e[N];
void tarjan(int x){
int&l=low[x];l=dfn[x]=++sum;s[++top]=x;
for(int i=0;i<e[x].size();i++)
if(!dfn[y]){
tarjan(y);l=min(l,low[y]);
if(low[y]>=dfn[x]){
vector<int> b;b.push_back(x);
while(s[top+1]!=y) b.push_back(s[top--]);
int cnt=0,sz=b.size();
for(int j=0;j<sz;j++) now[b[j]]=true;
for(int j=0;j<sz;j++)
for(int k=0;k<e[b[j]].size();k++)
if(now[e[b[j]][k]]) cnt++;
cnt/=2;if(sz>cnt) ans1+=cnt;if(sz<cnt) ans2+=cnt;
for(int j=0;j<sz;j++) now[b[j]]=false;
}
}
else l=min(l,dfn[y]);
}
void solve(){
sum=top=ans1=ans2=0;fill_n(s+1,n+1,0);
for(int i=0;i<n;i++) {e[i].clear();now[i]=dfn[i]=0;}
while(m--) {int u,v;scanf("%d%d",&u,&v);e[u].push_back(v);e[v].push_back(u);}
for(int i=0;i<n;i++)
if(!dfn[i]) tarjan(i);
printf("%d %d\n",ans1,ans2);
}
signed main(){
while(~scanf("%d%d",&n,&m)&&n) solve();
return 0;
}
upd::调出来了,又是栈的一些问题
[POI2008]BLO-Blockade
题意:
一个连通图,对每个点求去掉这个点会有多少对有序点变的不连通(包括这个点)。 \((1\le N\le10^5,1\le M\le5\times10^5)\)
代码:
#include <bits/stdc++.h>
#define y e[x][i]
using namespace std;
typedef long long ll;
const int N=1e5+5;
int n,m,sum,dfn[N],low[N],sz[N];
ll ans[N];
vector<int> e[N];
void tarjan(int x){
int&l=low[x],&s=sz[x]=1,c=0;l=dfn[x]=++sum;
for(int i=0;i<e[x].size();i++)
if(!dfn[y]){
tarjan(y);l=min(l,low[y]);s+=sz[y];
if(low[y]>=dfn[x]) {ans[x]+=1ll*sz[y]*c;c+=sz[y];}
}
else l=min(l,dfn[y]);
ans[x]+=1ll*(n-c-1)*c;
}
signed main(){
scanf("%d%d",&n,&m);
while(m--) {int u,v;scanf("%d%d",&u,&v);e[u].push_back(v);e[v].push_back(u);}
tarjan(1);
for(int i=1;i<=n;i++) printf("%lld\n",(ans[i]+n-1)*2);
return 0;
}
SP2878 KNIGHTS - Knights of the Round Table
题意:
将 \(N\) 个点放成一个长度为奇数且不为一的环,有 \(M\) 对点不能相邻,求有哪些点一定无法被放进环中。 \((1\le N\le10^3,1\le M\le10^6)\)
题解:
考虑建出这个图的补图,则有边相邻的点才可以放在一起,则问题转化为求这个点是否被包含在奇环中。
如果不在一个点双里,就肯定不会有公共的环,因此每个点双内部考虑。
只要对每个点双进行二分图染色,如果有奇环点双内每个点都可以被放进环中。
时间复杂度: \(O(M+N^2)\)
代码:
#include <bits/stdc++.h>
using namespace std;
const int N=1005;
int n,m,sum,top,s[N],dfn[N],low[N],c[N];
bool flag,vis[N],ans[N],e[N][N];
vector<vector<int> > b;
void tarjan(int x){
int&l=low[x];l=dfn[x]=++sum;s[++top]=x;
for(int i=1;i<=n;i++)
if(x!=i&&e[x][i]){
if(!dfn[i]){
tarjan(i);l=min(l,low[i]);
if(low[i]>=dfn[x]){
vector<int> bcc;bcc.push_back(x);
while(s[top+1]!=i) bcc.push_back(s[top--]);
b.push_back(bcc);
}
}
else l=min(l,dfn[i]);
}
}
void dfs(int x,int v){
c[x]=v;
for(int i=1;i<=n&&!flag;i++)
if(vis[i]&&x!=i&&e[x][i]){
if(c[i]==v) flag=1;
if(!c[i]) dfs(i,3-v);
}
}
void solve(){
sum=top=0;b.clear();
for(int i=1;i<=n;i++) {s[i]=dfn[i]=ans[i]=0;fill_n(e[i]+1,n,1);}
while(m--) {int u,v;scanf("%d%d",&u,&v);e[u][v]=e[v][u]=0;}
for(int i=1;i<=n;i++)
if(!dfn[i]) tarjan(i);
for(int i=0;i<b.size();i++){
fill_n(vis+1,n,0);fill_n(c+1,n,0);flag=0;
for(int j=0;j<b[i].size();j++) vis[b[i][j]]=1;
dfs(b[i][0],1);
for(int j=0;j<b[i].size();j++){
vis[b[i][j]]=0;
if(flag) ans[b[i][j]]=1;
}
}
printf("%d\n",count(ans+1,ans+n+1,0));
}
signed main(){
while(~scanf("%d%d",&n,&m)&&n&&m) solve();
return 0;
}
NOI Online #1 提高组 冒泡排序
题意:
给定一个长为 \(N\) 的排列,有 \(Q\) 次操作:
-
1 x:将排列的第 \(x\) 个数和第 \(x+1\) 个数交换。 -
2 k:求经过 \(k\) 轮冒泡排序的逆序对个数
\((1\le N,Q\le 2\times10^5,0\le k<2^ {31})\)
代码:
#include <bits/stdc++.h>
#define ii inline
using namespace std;
typedef long long ll;
const int N=2e5+5;
int n,m,a[N],b[N],c[N];
ll ans,tree[N];
ii int lb(int x) {return x&(-x);}
ii void add(int x,ll v) {while(x<=n) {tree[x]+=v;x+=lb(x);}}
ii ll ask(int x) {ll res=0;while(x) {res+=tree[x];x-=lb(x);}return res;}
signed main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) {scanf("%d",&a[i]);b[i]=i-1-ask(a[i]);ans+=b[i];c[b[i]]++;add(a[i],1);}
memset(tree,0,sizeof(tree));add(1,ans);
for(int i=0,tot=0;i<n;i++) {tot+=c[i];add(i+2,tot-n);}
while(m--){
int opt,x;scanf("%d%d",&opt,&x);x=min(x,n-1);
if(opt==1){
swap(a[x],a[x+1]);swap(b[x],b[x+1]);
if(a[x]>a[x+1]) {add(1,1);add(b[x+1]+2,-1);b[x+1]++;}
else {add(1,-1);b[x]--;add(b[x]+2,1);}
}
else printf("%lld\n",ask(x+1));
}
return 0;
}
NOI Online #3 提高组 魔法值
题意:
\(N\) 个城市,起初有魔法值 \(F_i\) , \(M\) 条有向边,每天每个城市的魔法值会变成它的入点魔法值异或和, \(Q\) 次询问,每次询问 \(A_i\) 天城市一的魔法值。 \((1≤N,Q≤100,1\le M\le\frac{N(N-1)}{2},1\le A_i\le 2^{32},0\le F_i\le2^{32})\)
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=105,L=32;
int n,m,q;
struct mat{
int x,y;ll b[N][N];
mat operator * (const mat&t) const {
mat res;res.x=x;res.y=t.y;
for(int i=1;i<=x;i++)
for(int j=1;j<=t.y;j++){
res.b[i][j]=0;
for(int k=1;k<=y;k++) res.b[i][j]^=b[i][k]*t.b[k][j];
}
return res;
}
}g,f[L];
signed main(){
scanf("%d%d%d",&n,&m,&q);g.x=1;f[0].x=f[0].y=g.y=n;
for(int i=1;i<=n;i++) scanf("%lld",&g.b[1][i]);
while(m--) {int u,v;scanf("%d%d",&u,&v);f[0].b[u][v]=f[0].b[v][u]=1;}
for(int i=1;i<L;i++) f[i]=f[i-1]*f[i-1];
while(q--){
int x;scanf("%lld",&x);mat ans=g;
for(ll i=0;i<L;i++)
if(x>>i&1ll) ans=ans*f[i];
printf("%lld\n",ans.b[1][1]);
}
return 0;
}

浙公网安备 33010602011771号