10.25

图论:欧拉回路,缩点, 2-SAT

一些 NOI Online

UVA12118 检查员的难题

题意:

\(N\) 个点的无向完全图,可以重复经过边,求经过指定的边最少要经过多少条边。 \((1\le N\le1000)\)

代码:

#include <bits/stdc++.h>
using namespace std;
const int N=1005;
int tot,n,m,k,ans;
bool flag,vis[N];
vector<int> e[N];
int dfs(int x){
	if(vis[x]) return 0;vis[x]=true;int res=e[x].size()%2;
	for(int i=0;i<e[x].size();i++) res+=dfs(e[x][i]);return res;
}
void solve(){
	ans=m;flag=false;fill_n(vis+1,n,false);
	for(int i=1;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(int i=1;i<=n;i++)
		if(!vis[i]&&!e[i].empty()) {ans+=max((dfs(i)-2)/2,0);if(flag) ans++;flag=true;}
	printf("Case %d: %d\n",++tot,ans*k);
}
signed main(){
	while(~scanf("%d%d%d",&n,&m,&k)&&(n||m||k)) solve();
	return 0;
}

POJ2186 受欢迎的牛

题意:

给定一个 \(N\) 个点 \(M\) 条边的有向图,求有多少个点是所有点都可以到达的。 \((1\le N\le10^4,1\le M\le5\times10^4)\)

题解:

首先一个强连通分量里的点是不会影响结果的,先缩点。

接着构成了一个有向无环图,当有多个出度为 \(0\) 的点时则无解。否则答案就是那个出度为 \(0\) 的点包含的点数。

LA4287 等价性证明

题意:

求最少加几条边使得一张有向图强联通。 \((1\le N\le2\times10^4,1\le M\le5\times10^5)\)

题解:

首先将图给缩点,也考虑这个有向无环图。

只需要给没有入度的点和没有出度的点连边,发现只需要以数量少的为基准交叉连成一个环,剩下的多的随便连一下,则答案为这两种点中个数更多的。

如果两种都符合,就都加,仍然是正确的。注意特判只有一个点的情况。

UVA11324 最大团

题意:

求有向图最长的一条有向路径(不一定简单)。 \((1\le N\le10^3,1\le M\le5\times10^4)\)

代码:

发现一种写法:因为 tarjan 出来的编号倒序就是一个合法的拓扑序,所以在有向无环图上就不需要再拓扑排序一遍,直接从后往前扫。

#include <bits/stdc++.h>
#define y e[x][i]
#define to g[i][j]
using namespace std;
const int N=1005;
int t,n,m,tp,s1,s2,ans,s[N],dfn[N],low[N],bel[N],sum[N],f[N];
vector<int> e[N],g[N];
void tarjan(int x){
	int&l=low[x];dfn[x]=l=++s1;s[++tp]=x;
	for(int i=0;i<e[x].size();i++)
		if(!dfn[y]) {tarjan(y);l=min(l,low[y]);}
		else if(!bel[y]) l=min(l,dfn[y]);
	if(dfn[x]!=l) return;s2++;
	for(int i=0;i!=x;tp--,sum[s2]++) bel[i=s[tp]]=s2;
}
void solve(){
	scanf("%d%d",&n,&m);tp=s1=s2=ans=0;
	for(int i=1;i<=n;i++) {e[i].clear();g[i].clear();dfn[i]=low[i]=bel[i]=sum[i]=f[i]=0;}
	while(m--) {int u,v;scanf("%d%d",&u,&v);e[u].push_back(v);}
	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]) g[bel[x]].push_back(bel[y]);
	for(int i=s2;i;i--)
		for(int j=0;j<g[i].size();j++) f[to]=max(f[to],f[i]+sum[i]);
	for(int i=1;i<=s2;i++) ans=max(ans,f[i]+sum[i]);
	printf("%d\n",ans);
}
signed main(){
	scanf("%d",&t);while(t--) solve();
	return 0;
}

LOJ10092 最大半连通子图

题意:

求有向图最长的一条有向路径(不一定简单)并输出方案数模 \(X\)\((1\le N\le10^5,1\le M\le10^6,1\le X\le10^8)\)

题解:

求最长参考上面,方案数的时候如果能把最长的更新了,就直接转移过来,否则加上去即可。

LA3713 Astronauts

题意:

给出 \(N\) 小于 \(200\) 的数字,大于等于平均数的分到 A 或 C 组,小于的分到 B 或 C 组,有 \(M\) 个限制表示第 \(x\) 和第 \(y\) 个数不能分在一起,构造方案或判断无解。 \((1\le N,M\le10^5)\)

代码:

#include <bits/stdc++.h>
#define y e[x][i]
using namespace std;
const int N=2e5+5;
int t,n,m,k,tp,s1,s2,sum,a[N],s[N],dfn[N],low[N],bel[N];
vector<int> e[N];
void tarjan(int x){
	int&l=low[x];dfn[x]=l=++s1;s[++tp]=x;
	for(int i=0;i<e[x].size();i++)
		if(!dfn[y]) {tarjan(y);l=min(l,low[y]);}
		else if(!bel[y]) l=min(l,dfn[y]);
	if(dfn[x]!=l) return;s2++;
	for(int i=0;i!=x;tp--) bel[i=s[tp]]=s2;
}
void solve(){
	k=n*2;tp=s1=s2=sum=0;
	for(int i=1;i<=k;i++) {e[i].clear();dfn[i]=low[i]=bel[i]=0;}
	for(int i=1;i<=n;i++) {scanf("%d",&a[i]);sum+=a[i];}
	for(int i=1;i<=n;i++) a[i]=a[i]*n>=sum;
	while(m--){
		int u,v;scanf("%d%d",&u,&v);
		e[u+n].push_back(v);e[v+n].push_back(u);
		if(a[u]==a[v]) {e[u].push_back(v+n);e[v].push_back(u+n);}
	}
	for(int i=1;i<=k;i++)
		if(!dfn[i]) tarjan(i);
	for(int i=1;i<=n;i++)
		if(bel[i]==bel[i+n]) {puts("No solution.");return;}
	for(int i=1;i<=n;i++) puts(bel[i]<bel[i+n]?a[i]?"A":"B":"C");
}
signed main(){
	while(~scanf("%d%d",&n,&m)&&n&&m) solve();
	return 0;
}

NOI Online #1 提高组 序列

题意:

给出长为 \(N\) 序列 \(A\)\(B\) ,有 \(Q\) 个可以使用无限次的操作:

  • 1 u v :让 \(A_u,A_v\) 同时加 \(1\) 或减 \(1\)

  • 2 u v :让 \(A_u\)\(1\)\(A_v\)\(1\) ;或 \(A_u\)\(1\)\(A_v\)\(1\)

问序列 \(A\) 能否变成 \(B\)\((1\le N,Q\le 10^5)\)

代码

#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,s,a[N],b[N],p[N],q[N],f[N],c[N];
ll ss[3],val[N];
vector<int> e[N];
int getf(int x) {return f[x]==x?x:f[x]=getf(f[x]);}
bool dfs(int x,int k){
	c[x]=k;ss[k]+=val[x];bool res=true;
	for(int i=0;i<e[x].size();i++){
		if(!c[y]&&!dfs(y,3-k)) res=false;
		if(c[y]==k) res=false;
	}
	return res;
}
bool solve(){
	scanf("%d%d",&n,&m);s=0;
	for(int i=1;i<=n;i++) {scanf("%d",&a[i]);f[i]=i;val[i]=c[i]=0;e[i].clear();}
	for(int i=1;i<=n;i++) scanf("%d",&b[i]);
	while(m--){
		int opt,u,v;scanf("%d%d%d",&opt,&u,&v);
		if(opt==1) {p[++s]=u;q[s]=v;}
		else f[getf(u)]=getf(v);
	}
	for(int i=1;i<=n;i++) val[getf(i)]+=b[i]-a[i];
	for(int i=1;i<=s;i++) {int u=getf(p[i]),v=getf(q[i]);e[u].push_back(v);e[v].push_back(u);}
	for(int i=1;i<=n;i++)
		if(getf(i)==i&&!c[i]){
			ss[1]=ss[2]=0;bool flag=dfs(i,1);
			if(flag&&ss[1]!=ss[2]) return false;
			if(!flag&&(ss[1]+ss[2])%2) return false; 
		}
	return true;
}
signed main(){
	scanf("%d",&t);while(t--) puts(solve()?"YES":"NO");
	return 0;
}

NOI Online #1 提高组 最小环

题意:

在一个长度为 \(N\) 的环中,长度为 \(N\) 的序列 \(A\) 。给出 \(M\)\(K\) ,对于每一对距离为 \(K\) 的数对,有两数乘积的贡献。(这个数对有两次要都算),求出这 \(M\) 个答案。 \((1\le M\le N\le2\times 10^5,0\le K\le\frac{N}{2})\)

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5;
int n,m;
ll a[N],ans[N];
int gcd(int x,int y) {return y?gcd(y,x%y):x;}
ll solve(int k){
	if(!k){
		ll r=0;
		for(int i=1;i<=n;i++) r+=a[i]*a[i];
		return r;
	}
	int c=n/gcd(n,k);ll&s=ans[c];if(s) return s;
	for(int i=0;i<n;i+=c){
		s+=a[i+1]*a[i+2]+a[i+c-1]*a[i+c];
		for(int j=1;j<=c-2;j++) s+=a[i+j]*a[i+j+2];
	}
	return s;
}
signed main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
	sort(a+1,a+n+1,greater<ll>());
	while(m--) {int x;scanf("%d",&x);printf("%lld\n",solve(x));}
	return 0;
}

NOI Online #2 提高组 涂色游戏

题意:

给格子染色, \(p_1\) 倍数的染一种, \(p_2\) 倍数的染另一种,都是则两种都可以,没被染色的直接删除,求是否能让最长连续的一段小于等于 \(k\)

\(T\) 组数据。 \((1\le T\le10^6,1\le p_1,p_2,k\le10^9)\)

代码:

#include <bits/stdc++.h>
using namespace std;
int t,a,b,k;
int gcd(int x,int y) {return y?gcd(y,x%y):x;}
void solve(){
	scanf("%d%d%d",&a,&b,&k);
	if(k==1) {puts("NO");return;}
    if(b>a) swap(a,b);
	puts(((a-gcd(a,b)-1)/b+1)>=k?"NO":"YES");
}
signed main(){
	scanf("%d",&t);while(t--) solve();
    return 0;
}

NOI Online #2 提高组 子序列问题

题意:

给定长度为 \(N\) 序列 \(A\)

\(f(l,r)\) 为从 \(l\)\(r\) 不同的值得个数。

\(\sum_{l=1}^{n}\sum_{r=l}^{n}f(l,r)^2 \mod 10^9+7\)

\((1\le N\le10^6,1\le A_i\le 10^9)\)

代码:

#include <bits/stdc++.h>
#define ii inline
#define lc pos<<1
#define rc pos<<1|1
using namespace std;
const int N=1e6+5,mod=1e9+7;
int n,m,ans,a[N],b[N],lst[N],pre[N],t1[N<<2],t2[N<<2],tag[N<<2];
ii void pushup(int pos) {t1[pos]=(t1[lc]+t1[rc])%mod;t2[pos]=(t2[lc]+t2[rc])%mod;}
ii void add(int pos,int l,int r,int x) {tag[pos]+=x;(t2[pos]+=1ll*(r-l+1)*x*x%mod)%=mod;(t2[pos]+=2ll*x*t1[pos]%mod)%=mod;(t1[pos]+=1ll*(r-l+1)*x%mod)%=mod;}
ii void pushdown(int pos,int l,int r,int mid) {if(tag[pos]) {add(lc,l,mid,tag[pos]);add(rc,mid+1,r,tag[pos]);tag[pos]=0;}}
void update(int l,int r,int pos,int L,int R){
	if(L<=l&&r<=R) {add(pos,l,r,1);return;}int mid=(l+r)>>1;pushdown(pos,l,r,mid);
	if(L<=mid) update(l,mid,lc,L,R);if(R>mid) update(mid+1,r,rc,L,R);pushup(pos);
}
signed main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++) {scanf("%d",&a[i]);b[i]=a[i];}
	sort(b+1,b+n+1);m=unique(b+1,b+n+1)-b;
	for(int i=1;i<=n;i++) {a[i]=lower_bound(b+1,b+m,a[i])-b;pre[i]=lst[a[i]];lst[a[i]]=i;}
	for(int i=1;i<=n;i++) {update(1,n,1,pre[i]+1,i);(ans+=t2[1])%=mod;}
	printf("%d\n",ans);
	return 0;
}

另:为啥某些图片在电脑上看是好的在钉钉上是花的下载下来又清除了

posted @ 2020-10-28 21:43  shrtcl  阅读(60)  评论(0编辑  收藏  举报