2021春 算法复习

1、 [USACO12FEB]Cow Coupons G

题面:https://www.luogu.com.cn/problem/P3045

贪心+堆。首先在优惠券没用完的情况下按优惠价从小到大全部买进。对于没购买的奶牛i,若想加购,可花费原价Pi,或挪用另一只奶牛j的优惠券,即花费Ci+Pj-Cj。故维护三个小根堆(P,C,P-C)即可。(代码略丑 (h1,h2存的是编号,h3存的是值

洛谷的最后一个数据有错,读入n为2,实际为3

#include<iostream>
#include<algorithm>
using namespace std;
int n,k,n1,n2,n3,ans,a[50010],b[50010],h1[50010],h2[50010],h3[50010];
long long m;
bool canbuy[50010];
inline bool cmp1(int x,int y){return a[x]<a[y];}
inline bool cmp2(int x,int y){return b[x]<b[y];}
void down1(int x){
	int ls=x<<1,rs=(x<<1)+1;
	if (ls>n1) return;
	if (rs<=n1 && a[h1[rs]]<a[h1[ls]] && a[h1[rs]]<a[h1[x]]) swap(h1[x],h1[rs]),down1(rs);
	else if (a[h1[ls]]<a[h1[x]]) swap(h1[x],h1[ls]),down1(ls);
}

void down2(int x){
	int ls=x<<1,rs=(x<<1)+1;
	if (ls>n2) return;
	if (rs<=n2 && b[h2[rs]]<b[h2[ls]] && b[h2[rs]]<b[h2[x]]) swap(h2[x],h2[rs]),down2(rs);
	else if (b[h2[ls]]<b[h2[x]]) swap(h2[x],h2[ls]),down2(ls);
}

void down3(int x){
	int ls=x<<1,rs=(x<<1)+1;
	if (ls>n3) return;
	if (rs<=n3 && h3[rs]<h3[ls] && h3[rs]<h3[x]) swap(h3[x],h3[rs]),down3(rs);
	else if (h3[ls]<h3[x]) swap(h3[x],h3[ls]),down3(ls);
}

int main(){
		scanf("%d%d%lld\n",&n,&k,&m);
		for (int i=1;i<=n;i++){
			scanf("%d%d\n",&a[i],&b[i]);
			h1[i]=h2[i]=i;
			canbuy[i]=true;
		}
		sort(h1+1,h1+n+1,cmp1);
		sort(h2+1,h2+n+1,cmp2);
		n1=n2=n;
		n3=0;
		for (int i=1;i<=k && m>b[h2[i]];i++){
			ans++;
			m-=b[h2[i]];
			h3[++n3]=a[h2[i]]-b[h2[i]];
			canbuy[h2[i]]=false;
		}
		for (;n1 && !canbuy[h1[1]];) h1[1]=h1[n1--],down1(1);
		for (;n2 && !canbuy[h2[1]];) h2[1]=h2[n2--],down2(1);
		sort(h3+1,h3+n3+1);
		for(;(n1 && m>a[h1[1]]) || (n2 && m>b[h2[1]]+h3[1]);){
			ans++;
			if (n1 && m>a[h1[1]]){
				m-=a[h1[1]];
				canbuy[h1[1]]=false;
				h1[1]=h1[n1--];
				down1(1);
			
			}
			else{
				m-=b[h2[1]]+h3[1];
				canbuy[h2[1]]=false;
				h3[1]=a[h2[1]]-b[h2[1]];
				down3(1);
				h2[1]=h2[n2--];
				down2(1);
			}
			for (;n1 && !canbuy[h1[1]];) h1[1]=h1[n1--],down1(1);
			for (;n2 && !canbuy[h2[1]];) h2[1]=h2[n2--],down2(1);
		}
		printf("%d",ans);
}

2、[HDU1512]Monkey King

题面:https://www.luogu.com.cn/problem/P1456

左偏树。lj洛谷有多组数据不注明害我白debug半天

#include<iostream>
using namespace std;
int n,m,x,y,a[100010],ls[100010],rs[100010],f[100010],dis[100010];
int merge(int x,int y){
	if (!x || !y) return x+y;
	if (a[y]>a[x]) swap(x,y);
	rs[x]=merge(rs[x],y);
	f[rs[x]]=x;
	if (dis[ls[x]]<dis[rs[x]]) swap(ls[x],rs[x]);
	dis[x]=dis[rs[x]]+1;
	return x; 
}

int root(int x){
	for (;f[x];x=f[x]);
	return x;
}

void update(int x){
	a[x]>>=1;
	int t=merge(ls[x],rs[x]);
	f[t]=ls[x]=rs[x]=0;
	merge(x,t);
}

int main(){
		while(scanf("%d",&n)!=EOF){
			for (int i=1;i<=n;i++){
				scanf("%d\n",&a[i]);
				ls[i]=rs[i]=f[i]=dis[i]=0;
			}
			scanf("%d\n",&m);
			for (;m--;){
				scanf("%d%d\n",&x,&y);
				x=root(x);
				y=root(y);
				if (x==y){
					printf("-1\n");
					continue;
				}
				update(x);
				update(y);
				printf("%d\n",a[merge(root(x),root(y))]);
			}
		}
}

3、[洛谷2766] 最长不下降子序列问题

题面:https://www.luogu.com.cn/problem/P2766

网络流。经典题,将一个点分裂成两个点来限制自身的流量。题解别人写的更好:https://www.luogu.com.cn/problem/solution/P2766

#include<iostream>
using namespace std;
struct line{
	int to,f,op,next;
} l[300000];
int n,m,s,T,N,ans,a[510],f[510],last[1010],dis[1010],cnt[1010];

int sap(int x,int delta){
	if (x==T) return delta;
	int mindis=N,sum=0;
	for (int i=last[x];i;i=l[i].next){
		int y=l[i].to;
		if (l[i].f>0 && dis[x]==dis[y]+1){
			int f=sap(y,min(l[i].f,delta-sum));
			sum+=f;
			l[i].f-=f;
			l[l[i].op].f+=f;
			if (dis[0]>N || sum==delta) return sum;
		}
		if (l[i].f>0) mindis=min(mindis,dis[y]);
	}
	if (sum==0){
		cnt[dis[x]]--;
		if (!cnt[dis[x]]) dis[0]=N;
		else cnt[dis[x]=mindis+1]++;
	}
	return sum;
}

void link(int x,int y,int f){
	l[++m].to=y; l[m].f=f; l[m].op=m+1; l[m].next=last[x]; last[x]=m;
	l[++m].to=x; l[m].f=0; l[m].op=m-1; l[m].next=last[y]; last[y]=m;
}

int main(){
		scanf("%d\n",&n);
		for (int i=1;i<=n;i++) scanf("%d",&a[i]);
		for (int i=n;i;i--){
			f[i]=1;
			for (int j=n;j>i;j--)
				if (a[j]>=a[i]) f[i]=max(f[i],f[j]+1);
			s=max(s,f[i]);
		}
		printf("%d\n",s);
		T=N=2*n+1;
		for (int i=1;i<=n;i++){
			link(2*i-1,2*i,1);
			if (f[i]==s) link(0,2*i-1,1);
			if (f[i]==1) link(2*i,T,1);
			for (int j=i+1;j<=n;j++)
				if (f[j]+1==f[i] && a[j]>=a[i]) link(2*i,2*j-1,1);
		}
		while (dis[0]<=N) ans+=sap(0,1e9);
		printf("%d\n",ans);
		if (f[1]==s) link(0,1,1e9);
		link(T-1,T,1e9);
		link(1,2,1e9);
		link(T-2,T-1,1e9);
		for (int i=0;i<=T;i++) dis[i]=cnt[i]=0;
		while (dis[0]<N) ans+=sap(0,1e9);
		printf("%d\n",ans);
}

4、[USACO05NOV]Asteroids G

题面:https://www.luogu.com.cn/problem/P7368

匈牙利算法。因为每个点要么被行覆盖,要么被列覆盖,所以可将问题转化为二分图最小覆盖问题。又由König 定理知,二分图最小点覆盖包含的点数等于二分图最大匹配包含的边数,故可转化为二分图匹配问题。

#include<iostream>
#include<string.h>
using namespace std;
int n,m,k,x,y,ans,to[10010],nxt[10010],last[10010],match[10010];
bool vis[10010];
bool dfs(int x){
	for (int i=last[x];i;i=nxt[i])
		if (!vis[to[i]]){
			vis[to[i]]=true;
			if (!match[to[i]] || dfs(match[to[i]])){
				match[to[i]]=x;
				return true;
			}
		}
	return false;
}

int main(){
		scanf("%d%d\n",&n,&k);
		for (;k--;){
			scanf("%d%d\n",&x,&y);
			y+=n;
			to[++m]=y; nxt[m]=last[x]; last[x]=m;
			to[++m]=x; nxt[m]=last[y]; last[y]=m;
		}
		ans=0;
		for (int i=1;i<=n;i++){
			memset(vis,false,sizeof(vis));
			if (dfs(i)) ans++;
		}
		printf("%d",ans);
}

5、[NOIP2009 提高组] 最优贸易

题面:https://www.luogu.com.cn/problem/P1073

SPFA。最常见的做法是双向SPFA分别处理出源到每个点路径上最小点,和每个点到汇路径上最大点。因为懒我只写了一个SPFA,维护两个标记:minc[x]为从源点到x点所有可能路径上最小点,f[x]为从源到x(包括x)的路上买入并卖出能得到的最优解,为了保证卖出在买进之后,只在SPFA时用minc买入,x点卖出的方案来更新f[x],其实就是用到了动态规划的思想。

#include<iostream>
#include<vector>
using namespace std;
vector<int> g[100010];
int n,m,x,y,z,head,tail,c[100010],q[2000010],minc[100010],f[100010];
bool inq[100010];
int main(){
		scanf("%d%d\n",&n,&m);
		for (int i=1;i<=n;i++) scanf("%d",&c[i]);
		for (;m--;){
			scanf("%d%d%d\n",&x,&y,&z);
			g[x].push_back(y);
			if (z==2) g[y].push_back(x);
		}
		q[1]=head=tail=1;
		inq[1]=true;
		minc[1]=c[1];
		f[1]=0;
		for (int i=2;i<=n;i++) minc[i]=1e9;
		for (;head<=tail;head++){
			inq[x=q[head]]=false;
			for (int i=0;i<g[x].size();i++){
				y=g[x][i];
				bool updated=false;
				if (min(minc[x],c[y])<minc[y]) minc[y]=min(minc[x],c[y]),updated=true;
				if (max(f[x],c[y]-minc[y])>f[y]) f[y]=max(f[x],c[y]-minc[y]),updated=true;
				if (updated && !inq[y]) inq[q[++tail]=y]=true;
			}
		}
		printf("%d",f[n]);
}

6、[ZJOI2006]物流运输

题面:https://www.luogu.com.cn/problem/P1772

DP+SPFA。因为数据范围非常非常小所以可以随便跑SPFA。f[i]表示前i天最少代价,易得转移方程f[i]=min(f[i],f[j-1]+(i-j+1)*cost+k),其中cost即为第j天到第i天的可行最短路。注意:每个码头的禁用区间可能有多个,而且可能有重边

#include<iostream>
#include<string.h>
using namespace std;
int n,m,k,e,x,y,z,num,v[30][30],d[30],f[110],q[1000];
bool inq[30],working[30],ban[30][110];
int spfa(){
	memset(d,0x3f,sizeof(d));
	int head=1,tail=1;
	q[1]=1;
	d[1]=0;
	inq[1]=true;
	for (;head<=tail;head++){
		int x=q[head];
		for (int y=1;y<=m;y++)
			if (v[x][y]){
				if (!working[y]) continue;
				if (d[x]+v[x][y]<d[y]){
					d[y]=d[x]+v[x][y];
					if (!inq[y]) inq[q[++tail]=y]=true;
				}
		}
		inq[x]=false;
	}
	return d[m];
}

int main(){
		scanf("%d%d%d%d\n",&n,&m,&k,&e);
		memset(v,0x3f,sizeof(v));
		for (;e--;){
			scanf("%d%d%d\n",&x,&y,&z);
			v[x][y]=v[y][x]=min(v[x][y],z);
		}
		scanf("%d",&e);
		memset(ban,false,sizeof(ban));
		for (;e--;){
			scanf("%d%d%d",&x,&y,&z);
			for (int i=y;i<=z;i++) ban[x][i]=true;
		}
		for (int i=1;i<=m;i++) working[i]=true;
		memset(f,0x3f,sizeof(f));
		f[0]=-k;
		for (int i=1;i<=n;i++){
			memset(working,true,sizeof(working));
			for (int j=i;j;j--){
				for (x=1;x<=m;x++)
					if (ban[x][j]) working[x]=false;
				int cost=spfa();
				if (cost>1e9) break;
				f[i]=min(f[i],f[j-1]+(i-j+1)*cost+k);
			}
		}
		printf("%d",f[n]);
}

7、[USACO08OCT]Watering Hole G

题面:https://www.luogu.com.cn/problem/P1550

最小生成树。增设一个水源点0,每个牧场都与水源相连,权值为在该牧场挖水井的代价。然后跑最小生成树即可。代码为kruskal。

#include<iostream>
#include<algorithm>
using namespace std;
struct edge{
	int from,to,v;
} e[100000];
int n,m,ans,x,y,w[310],f[310];
inline bool cmp(edge a,edge b){
	return a.v<b.v;
}

int find(int x){
	if (f[x]==x) return x;
	return f[x]=find(f[x]);
}

int main(){
		scanf("%d\n",&n);
		for (int i=1;i<=n;i++){
			scanf("%d\n",&w[i]);
			f[i]=i;
			e[++m].from=0;
			e[m].to=i;
			e[m].v=w[i];
		}
		for (int i=1;i<=n;i++)
			for (int j=1;j<=n;j++){
				scanf("%d",&x);
				if (j>=i) continue;
				e[++m].from=i;
				e[m].to=j;
				e[m].v=x;
			}
		sort(e+1,e+m+1,cmp);
		for (int i=1;i<=m;i++){
			x=find(e[i].from);
			y=find(e[i].to);
			if (x==y) continue;
			f[x]=y;
			ans+=e[i].v;
		}
		printf("%d",ans);
}

8、[USACO08OCT]Pasture Walking G

题面:https://www.luogu.com.cn/problem/P2912

lca裸题。记录x到根的距离dis[x],则解为dis[x]+dis[y]-2*dis[lca(x,y)]

#include<iostream>
using namespace std;
int n,m,q,x,y,z,to[2010],nxt[2010],v[2010],fi[1010],fa[1010][10],dis[1010],dep[1010];
bool vis[1010];
void dfs(int x){
	vis[x]=true;
	for (int i=1;i<=9;i++){
		if ((1<<i)>dep[x]) break;
		fa[x][i]=fa[fa[x][i-1]][i-1];
	}
	for (int i=fi[x];i;i=nxt[i]){
		int y=to[i];
		if (vis[y]) continue;
		fa[y][0]=x;
		dis[y]=dis[x]+v[i];
		dep[y]=dep[x]+1;
		dfs(y);
	}
}

int lca(int x,int y){
	if (dep[x]<dep[y]) swap(x,y);
	int d=dep[x]-dep[y];
	for (int i=0;i<=9;i++)
		if ((1<<i)&d) x=fa[x][i];
	for (int i=9;i>=0;i--)
		if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
	if (x!=y) return fa[x][0];
	return x;
}

int main(){
		scanf("%d%d\n",&n,&q);
		for (int i=1;i<n;i++){
			scanf("%d%d%d\n",&x,&y,&z);
			to[++m]=y; nxt[m]=fi[x]; v[m]=z; fi[x]=m;
			to[++m]=x; nxt[m]=fi[y]; v[m]=z; fi[y]=m;
		}
		dfs(1);
		for (;q--;){
			scanf("%d%d\n",&x,&y);
			printf("%d\n",dis[x]+dis[y]-(dis[lca(x,y)]<<1));
		}
}

9、[NOI2005] 瑰丽华尔兹

题面:https://www.luogu.com.cn/problem/P2254

DP+单调队列优化。老朋友了哈哈哈,第几次做都记不清了,复习dp必备。若设计状态为f[t][i][j]表示t时刻走到点(i,j)的最长路径,从 f[t-1][前一个时刻的位置] 转移,则时间复杂度为O(kN^3)。故选择用单调队列来优化,状态重设为f[k][i][j]表示第k个时间区间结束后走到点(i,j)的最长路径。

#include<iostream>
#include<string.h>
using namespace std;
int dx[5]={0,-1,1,0,0};
int dy[5]={0,0,0,-1,1};
int n,m,x,y,k,d,ans,f[210][210][210],g[210],pos[210],q[210];
char c[210][210];
void dp(int k,int d,int x,int y,int l){
	int head=1,tail=0;
	for (int i=1;x>=1 && x<=n && y>=1 && y<=m;i++,x+=dx[d],y+=dy[d]){
		if (c[x][y]=='x'){
			head=1,tail=0;
			continue;
		} 
		if (f[k-1][x][y]>=0){
			while (head<=tail && i-pos[tail]+g[tail]<f[k-1][x][y]) tail--;
			pos[++tail]=i;
			g[tail]=f[k-1][x][y];
		}
		while (head<=tail && i-pos[head]>l) head++;
		if (head<=tail) f[k][x][y]=g[head]+i-pos[head];
		else f[k][x][y]=-1e9;
		ans=max(ans,f[k][x][y]);
	}
}

int main(){
		scanf("%d%d%d%d%d\n",&n,&m,&x,&y,&k);
		for (int i=1;i<=n;i++,scanf("\n"))
			for (int j=1;j<=m;j++) scanf("%c",&c[i][j]);
		memset(f,0xf3,sizeof(f));
		f[0][x][y]=0;
		for (int i=1;i<=k;i++){
			scanf("%d%d%d\n",&x,&y,&d);
			if (d==1)
				for (int j=1;j<=m;j++) dp(i,d,n,j,y-x+1);
			if (d==2)
				for (int j=1;j<=m;j++) dp(i,d,1,j,y-x+1);
			if (d==3)
				for (int j=1;j<=n;j++) dp(i,d,j,m,y-x+1);
			if (d==4)
				for (int j=1;j<=n;j++) dp(i,d,j,1,y-x+1);
		}
		printf("%d",ans);
}

10、[SCOI2005]最大子矩阵

题面:https://www.luogu.com.cn/problem/P2331

DP。分m=1,m=2两种情况讨论。m=1时设状态f[i][k]为前i行k个矩阵最优解,sum[i]为前i个数之和,则转移方程f[i][k]=max(f[i][k],f[j][k-1]+sum[i]-sum[j])。m=2时,设状态g[i][j][k]为第一列前i个和第二列前j个取了k个矩阵的最优解,转移方程见代码。

#include<iostream>
using namespace std;
int n,m,K,sum[110],sum1[110],sum2[110],f[110][11],g[110][110][11];
int main(){
		scanf("%d%d%d\n",&n,&m,&K);
		if (m==1){
			for (int i=1;i<=n;i++){
				scanf("%d",&sum[i]);
				sum[i]+=sum[i-1];
			}
			for (int i=1;i<=n;i++)
				for (int k=1;k<=K;k++){
					f[i][k]=f[i-1][k];
					for (int j=0;j<i;j++)
						f[i][k]=max(f[i][k],f[j][k-1]+sum[i]-sum[j]);
				}
			printf("%d",f[n][K]);
		}
		else{
			for (int i=1;i<=n;i++){
				scanf("%d%d\n",&sum1[i],&sum2[i]);
				sum1[i]+=sum1[i-1];
				sum2[i]+=sum2[i-1];
			}
			for (int i=1;i<=n;i++)
				for (int j=1;j<=n;j++)
					for (int k=1;k<=K;k++){
						g[i][j][k]=max(g[i-1][j][k],g[i][j-1][k]);
						for (int l=0;l<i;l++) g[i][j][k]=max(g[i][j][k],g[l][j][k-1]+sum1[i]-sum1[l]);
						for (int l=0;l<j;l++) g[i][j][k]=max(g[i][j][k],g[i][l][k-1]+sum2[j]-sum2[l]);
						if (i==j)
							for (int l=0;l<i;l++) g[i][j][k]=max(g[i][j][k],g[l][l][k-1]+sum1[i]-sum1[l]+sum2[j]-sum2[l]);
					}
			printf("%d",g[n][n][K]);
		}
}

11、[SCOI2005]互不侵犯

题面:https://www.luogu.com.cn/problem/P1896

状压DP。先预处理一下可能出现的状态再dp。f[k][i][x]表示前k层,第k层状态为i,已经放了x个国王的解数。

#include<iostream>
#include<string.h>
using namespace std;
long long ans,f[10][1000][90];
int n,K,state[1000];
bool legal[1000],c[1000][1000];
void build(int x,int p,int num,bool last){
	if (p==n){
		legal[x]=true;
		state[x]=num;
		return;
	}
	if (!last) build((x<<1)+1,p+1,num+1,true);
	build(x<<1,p+1,num,false);
}

bool check(int x,int y){
	if (x&y) return false;
	for (x=x|y;x;x>>=1)
		if ((x&3)==3) return false;
	return true;
}

int main(){
		scanf("%d%d\n",&n,&K);
		build(0,0,0,false);
		f[0][0][0]=1;
		memset(c,false,sizeof(c));
		for (int i=(1<<n)-1;i>=0;i--)
			if (legal[i])
				for (int j=(1<<n)-1;j>i;j--)
					if (legal[j] && check(i,j)) c[i][j]=c[j][i]=true;
		c[0][0]=true;
		for (int k=1;k<=n;k++)
			for (int i=(1<<n)-1;i>=0;i--)
				if (legal[i])
					for (int j=(1<<n)-1;j>=0;j--)
						if (legal[j] && c[i][j])
							for (int x=state[i];x<=K;x++)
								f[k][i][x]+=f[k-1][j][x-state[i]];
		for (int i=(1<<n)-1;i>=0;i--) ans+=f[n][i][K];
		printf("%lld",ans);
}

12、[ZJOI2008]骑士

题面:https://www.luogu.com.cn/problem/P2607

树上DP。为啥是树呢,因为对于每个连通块,都有x个点和x条边,所以每个连通块中都有且仅有一个环。环中随便找一条边拆开,两个端点分别为树根各跑一遍DP即可。状态f[x][0/1],0/1表示取/不取x。

#include<iostream>
using namespace std;
struct edge{
	int to,next;
	bool ban;
} e[2000010];
long long t,ans,f[1000010][2];
int n,m,x,root1,root2,v[1000010],fi[1000010];
bool vis[1000010];
void dfs(int x,int fa){
	vis[x]=true;
	for (int i=fi[x];i;i=e[i].next)
		if (!vis[e[i].to]) dfs(e[i].to,x);
		else if (e[i].to!=fa){
			root1=x;
			root2=e[i].to;
			e[i].ban=true;
			if (i&1) e[i+1].ban=true;
			else e[i-1].ban=true;
		}
}

void dp(int x,int fa){
	f[x][0]=f[x][1]=0;
	for (int i=fi[x];i;i=e[i].next)
		if (!e[i].ban && e[i].to!=fa){
		int y=e[i].to;
		dp(y,x);
		f[x][0]+=max(f[y][0],f[y][1]);
		f[x][1]+=f[y][0];
	}
	f[x][1]+=v[x];
}

int main(){
		scanf("%d\n",&n);
		for (int i=1;i<=n;i++){
			scanf("%d%d\n",&v[i],&x);
			e[++m].to=x; e[m].next=fi[i]; fi[i]=m;
			e[++m].to=i; e[m].next=fi[x]; fi[x]=m;
		}
		for (int i=1;i<=n;i++)
			if (!vis[i]){
				dfs(i,0);
				dp(root1,0);
				t=f[root1][0];
				dp(root2,0);
				ans+=max(t,f[root2][0]);
			}
		printf("%lld",ans);
}

13、[USACO03FALL][HAOI2006]受欢迎的牛 G

题面:https://www.luogu.com.cn/problem/P2341

Tarjan。若图中只有一个出度为0的强连通分量,则该强连通分量中所有奶牛都是明星奶牛。其他情况下都没有明星奶牛。

#include<iostream>
#include<stack>
#include<string.h>
using namespace std;
stack<int> s;
int n,m,en,cn,x,y,num,ans,to[50010],nxt[50010],fi[10010],dfn[10010],low[10010],cpn[10010],siz[10010];
bool last[10010];

void dfs(int x){
	dfn[x]=low[x]=++num;
	s.push(x);
	for (int i=fi[x];i;i=nxt[i]){
		int y=to[i];
		if (!dfn[y]){
			dfs(y);
			low[x]=min(low[x],low[y]);
		}
		else if (!cpn[y]) low[x]=min(low[x],dfn[y]);
	}
	if (dfn[x]==low[x]){
		cn++;
		while (1){
			int y=s.top();
			cpn[y]=cn;
			siz[cn]++;
			s.pop();
			if (y==x) break;
		}
	}
}

int main(){
		scanf("%d%d\n",&n,&m);
		for (;m--;){
			scanf("%d%d\n",&x,&y);
			to[++en]=y; nxt[en]=fi[x]; fi[x]=en;
		}
		for (int i=1;i<=n;i++)
			if (!dfn[i]) dfs(i);
		memset(last,true,sizeof(last));
		for (int i=1;i<=n;i++)
			if (last[cpn[i]])
				for (int j=fi[i];j;j=nxt[j])
					if (cpn[to[j]]!=cpn[i]){
						last[cpn[i]]=false;
						break;
					}
		for (int i=1;i<=cn;i++)
			if (last[i]){
				if (!ans) ans=siz[i];
				else{
					printf("0");
					return 0;
				}
			}
		printf("%d",ans);
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值