pyyzDay15

图论

[GXOI/GZOI2019] 旅行者

二进制分组+超级原点

跑最短路

[ARC173D] Bracket Walk

对每条边赋边权+1/-1

显然,若一个环上边权和为0,则一定能走出合法序列

直接判断是否有正负环

都有或都没有则能走出合法序列

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m;
int maxx[4008],minn[4008];
int l[8008],r[8008],w[8008];
signed main()
{
	//freopen("filename.in", "r", stdin);
	//freopen("filename.out", "w", stdout);
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		cin>>l[i]>>r[i];
		char s;
		cin>>s;
		if(s=='(') w[i]=1;
		else w[i]=-1;
	}
	memset(minn,0x3f,sizeof(minn));
	minn[1]=0;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			minn[r[j]]=min(minn[r[j]],minn[l[j]]+w[j]);
		}
	}
	memset(maxx,-0x3f,sizeof(maxx));
	maxx[1]=0;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			maxx[r[j]]=max(maxx[r[j]],maxx[l[j]]+w[j]);
		}
	}
	if(maxx[1]<=0&&minn[1]>=0) cout<<"Yes"<<'\n';
	else if(maxx[1]>0&&minn[1]<0) cout<<"Yes"<<'\n';
	else cout<<"No"<<'\n';
	return 0;
}

Run for beer

考虑贪心

一定尽量少经过点

在此基础上倒着贪心选数值小的

[ABC077D] Small Multiple

考虑拆分条件

对K取模,然后在模意义下跑最短路

dis[(i*10+j)%K]=dis[i]+j

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<bits/stdc++.h>
#define int long long
using namespace std;
int dis[100005];
priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > h;
vector<pair<int,int> > tu[100005];
signed main()
{
	//freopen("filename.in", "r", stdin);
	//freopen("filename.out", "w", stdout);
	int n;
	cin>>n;
	for(int i=0;i<n;i++){
		for(int j=0;j<=9;j++){
			tu[i].push_back({(i*10+j)%n,j});
		}
	}
	for(int i=1;i<=9;i++) tu[n].push_back({i%n,i});
	memset(dis,0x7f,sizeof(dis));
	dis[n]=0;
	h.push({0,n});
	while(!h.empty()){
		int x=h.top().second;
		int y=h.top().first;
		h.pop();
		if(dis[x]!=y) continue;
		for(auto e:tu[x]){
			if(dis[e.first]<=dis[x]+e.second) continue;
			dis[e.first]=dis[x]+e.second;
			h.push({dis[e.first],e.first});
		}
	}
	cout<<dis[0]<<'\n';
	return 0;
}

Edge Deletion

先删非最短路的边

变成一棵树

然后删叶子

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<bits/stdc++.h>
#define int long long
using namespace std;
int dis[300005];
priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > h;
vector<pair<int,int> > tu[300005];
vector<int> tt[300005];
int l[300005],r[300005],w[300005];
int ans[300005],an[300005],sum,du[300005],vis[300005],vi[300005];
queue<int> q;
signed main()
{
	//freopen("filename.in", "r", stdin);
	//freopen("filename.out", "w", stdout);
	int n,m,k;
	cin>>n>>m>>k;
	for(int i=1;i<=m;i++){
		cin>>l[i]>>r[i]>>w[i];
		tu[l[i]].push_back({r[i],w[i]});
		tu[r[i]].push_back({l[i],w[i]});
	}
	memset(dis,0x7f,sizeof(dis));
	dis[1]=0;
	h.push({0,1});
	while(!h.empty()){
		int x=h.top().second;
		int y=h.top().first;
		h.pop();
		if(dis[x]!=y) continue;
		for(auto e:tu[x]){
			if(dis[e.first]<=dis[x]+e.second) continue;
			dis[e.first]=dis[x]+e.second;
			h.push({dis[e.first],e.first});
		}
	}
	for(int i=1;i<=m;i++){
		if(dis[l[i]]+w[i]==dis[r[i]]||dis[r[i]]+w[i]==dis[l[i]]){
			if(dis[l[i]]+w[i]==dis[r[i]]){
				if(vi[r[i]]) continue;
				vi[r[i]]=1;
			}
			else{
				if(vi[l[i]]) continue;
				vi[l[i]]=1;
			}
			tt[l[i]].push_back(r[i]);
			tt[r[i]].push_back(l[i]);
			du[l[i]]++;
			du[r[i]]++;
			ans[++sum]=i;
		}
	}
	if(sum<=k){
		cout<<sum<<'\n';
		for(int i=1;i<=sum;i++){
			cout<<ans[i]<<' ';
		}
		cout<<'\n';
		return 0;
	}
	int cha=sum-k;
	for(int i=1;i<=n;i++){
		if(du[i]==1){
			q.push(i);
		}
		if(!du[i]) vis[i]=1;
	}
	int cn=0;
	while(!q.empty()&&cn<cha){
		int u=q.front();
		q.pop();
		if(vis[u]||du[u]!=1) continue;
		cn++;
		vis[u]=1;
		for(auto ed:tt[u]){
			if(!vis[ed]){
				du[ed]--;
				if(du[ed]==1){
					q.push(ed);
				}
			}
		}
	}
	cout<<k<<'\n';
	for(int i=1;i<=sum;i++){
		if(!vis[l[ans[i]]]&&!vis[r[ans[i]]]){
			cout<<ans[i]<<' ';
		}
	}
	cout<<'\n';
	return 0;
}

(未过)

Indecisive Taxi Fee

分讨

考虑修改的边是否是原最短路

image

2/3情况easy

4情况:考虑经过这条边的最短路,是好做的

1情况:较麻烦

考虑不经过这条边的最短路

需要线段树维护

[HAOI2012] 道路

枚举起点

对每条边算贡献

乘法原理

#include<iostream>
#include<cstdio>
#include<bits/stdc++.h>
#define int long long
using namespace std;
int dis[1505];
priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > q;
vector<pair<int,pair<int,int> > > tu[1505];
int dag[1505],cnt;
int l[5005],r[5005],w[5005];
int f[1505],g[1505],ans[5005],vis[1505];
inline 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-48;ch=getchar();}
	return x*f;
}
const int MOD=1e9+7;
signed main(){
	int n=read(),m=read();
	for(int i=1;i<=m;i++){
		l[i]=read(),r[i]=read(),w[i]=read();
		tu[l[i]].push_back({r[i],{w[i],i}});
	}
	for(int i=1;i<=n;i++){
		memset(dis,0x7f,sizeof(dis));
		memset(f,0,sizeof(f));
		memset(g,0,sizeof(g));
		memset(vis,0,sizeof(vis));
		cnt=0;
		dis[i]=0;
		f[i]=1;
		q.push({0,i});
		while(!q.empty()){
			int u=q.top().second;
			q.pop();
			if(vis[u]) continue;
			vis[u]=1;
			dag[++cnt]=u;
			for(auto ed:tu[u]){
				if(dis[u]+ed.second.first<dis[ed.first]){
					dis[ed.first]=dis[u]+ed.second.first;
					f[ed.first]=f[u];
					q.push({dis[ed.first],ed.first});
				}
				else if(dis[u]+ed.second.first==dis[ed.first]){
					f[ed.first]=(f[u]+f[ed.first])%MOD;
				}
			}
		}
		for(int j=cnt;j;j--){
			g[dag[j]]=1;
			for(auto ed:tu[dag[j]]){
				if(dis[dag[j]]+ed.second.first==dis[ed.first]){
					g[dag[j]]=(g[dag[j]]+g[ed.first])%MOD;
					ans[ed.second.second]=(ans[ed.second.second]+g[ed.first]*f[dag[j]]%MOD)%MOD;
				}
			}
		}
	}
	for(int i=1;i<=m;i++) cout<<ans[i]<<'\n';
	return 0;
}

image

image

[ABC355F] MST Query

发现权值只有10

于是每个权值建一个图层

每次修改,看联通了哪两个联通块

查找这两个联通块在图层几联通

计算差值

#include<iostream>
#include<cstdio>
#include<bits/stdc++.h>
#define int long long
using namespace std;
int f[200005][15];
int find(int x,int ceng){
	if(f[x][ceng]==x) return x;
	return f[x][ceng]=find(f[x][ceng],ceng);
}
void merge(int x,int y,int ceng){
	f[find(x,ceng)][ceng]=find(y,ceng);
}
signed main(){
	int n,q;
	cin>>n>>q;
	int ans=0;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=10;j++){
			f[i][j]=i;
		}
	}
	for(int i=1;i<n;i++){
		int l,r,w;
		cin>>l>>r>>w;
		ans+=w;
		for(int j=w;j<=10;j++){
			merge(l,r,j);
		}
	}
	while(q--){
		int l,r,w;
		cin>>l>>r>>w;
		int zh=0;
		for(int i=1;i<=10;i++){
			if(find(l,i)==find(r,i)){
				zh=i;
				break;
			}
		}
		if(zh>w){
			for(int j=w;j<zh;j++){
				merge(l,r,j);
			}
			ans-=zh;
			ans+=w;
		}
		cout<<ans<<'\n';
	}
	return 0;
}

[AGC016D] XOR Replace

发现可替换的只有n+1个元素:原序列所有元素+原序列异或和

则替换后的序列与之前的序列排序后必定相同

考虑图论建模

对每个点,ai <-> bi

答案即为联通块个数+连的边数-1(若原序列异或和对应的点不在任何一个联通快内,答案不要-1)

注意值域巨大,开map存vector

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<bits/stdc++.h>
#define int long long
#define jiaa(a,b) {a+=b;if(a>=MOD) a-=MOD;}
#define jian(a,b) {a-=b;if(a<0) a+=MOD;}
using namespace std;
int ksm(int a,int b,int p){
	if(b==0) return 1;
	if(b==1) return a%p;
	int c=ksm(a,b/2,p);
	c=c*c%p;
	if(b%2==1) c=c*a%p;
	return c%p;	
}
inline 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-48;ch=getchar();}
	return x*f;
}
int a[100005],b[100005],la[100005],lb[100005];
int ans;
map<int,int> vis;
map<int,vector<int> > tu;
void dfs(int x){
	vis[x]=1;
	for(auto ed:tu[x]){
		if(!vis[ed]) dfs(ed);
	}
}
signed main()
{
	//freopen("filename.in", "r", stdin);
	//freopen("filename.out", "w", stdout);
	int n=read();
	int sua=0,sub=0;
	for(int i=1;i<=n;i++){
		a[i]=read();
		la[i]=a[i];
		sua^=a[i];
	}
	for(int i=1;i<=n;i++){
		b[i]=read();
		lb[i]=b[i];
		sub^=b[i];
	}	
	a[++n]=sua;
	b[n]=sub;
	la[n]=sua;
	lb[n]=sub;
	sort(la+1,la+n+1);
	sort(lb+1,lb+n+1);
	for(int i=1;i<=n;i++){
		if(la[i]!=lb[i]){
			cout<<"-1"<<'\n';
			return 0;
		}
	}
	for(int i=1;i<n;i++){
		if(a[i]==b[i]) continue;
		tu[a[i]].push_back(b[i]);
		tu[b[i]].push_back(a[i]);
		ans++;
	}
	for(int i=1;i<n;i++){
		if(!vis[a[i]]&&a[i]!=b[i]){
			dfs(a[i]);
			ans++;
		}
	}
	if(tu[a[n]].size()) ans--;
	cout<<ans<<'\n';
	return 0;
}

[AGC001F] Wide Swap

考虑用值映射位置

冒泡排序

但复杂度炸了

考虑归并排序

判断什么时候j能排在i前面

差分约束

Xa-Xb<c -> Xa-Xb<=c-1(整数情况)

Xa=Xb -> Xa-Xb>=0&&Xb-Xa>=0

[1007] 倍杀测量者

发现除法并不好做

取对数变成减法

直接二分T+差分约束

[SCOI2011] 糖果

化简5个条件即可

注意SPFA会被卡

要写Tarjan缩点+拓扑排序

[省选联考 2021 A 卷] 矩阵游戏

先考虑构造

将最后一列和最后一行全填0

倒着填数

但问题是可能有数>1e6

考虑对每列每行+/-某个数(相邻的行列+-相反)

image

发现有+x+y/-x-y不好差分约束

于是将偶数行的x取反,奇数列的y取反

变成

image

差分约束判断即可

[SCOI2008] 天平

求出每个砝码的最小重量和最大重量

比较即可

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<bits/stdc++.h>
#define int long long
#define jiaa(a,b) {a+=b;if(a>=MOD) a-=MOD;}
#define jian(a,b) {a-=b;if(a<0) a+=MOD;}
using namespace std;
int ksm(int a,int b,int p){
	if(b==0) return 1;
	if(b==1) return a%p;
	int c=ksm(a,b/2,p);
	c=c*c%p;
	if(b%2==1) c=c*a%p;
	return c%p;	
}
inline 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-48;ch=getchar();}
	return x*f;
}
int maxx[55][55],minn[55][55];
signed main()
{
	//freopen("filename.in", "r", stdin);
	//freopen("filename.out", "w", stdout);
	memset(minn,0x3f,sizeof(minn));
	memset(maxx,-0x3f,sizeof(maxx));
	int n=read(),A=read(),B=read();
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			char s;
			cin>>s;
			if(i==j||s=='='){
				maxx[i][j]=minn[i][j]=0;
			}
			else if(s=='+'){
				maxx[i][j]=2;
				minn[i][j]=1;
			}
			else if(s=='-'){
				maxx[i][j]=-1;
				minn[i][j]=-2;
			}
			else{
				maxx[i][j]=2;
				minn[i][j]=-2;
			}
		}
	}
	for(int k=1;k<=n;k++){
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				maxx[i][j]=min(maxx[i][j],maxx[i][k]+maxx[k][j]);
				minn[i][j]=max(minn[i][j],minn[i][k]+minn[k][j]);
			}
		}
	}
	int c1=0,c2=0,c3=0;
	for(int i=1;i<=n;i++){
		for(int j=i+1;j<=n;j++){
			if(i==A||i==B||j==B||j==A) continue;
			if(minn[A][i]>maxx[j][B]||minn[A][j]>maxx[i][B]) c1++;
			if((minn[A][i]==maxx[A][i]&&minn[j][B]==maxx[j][B]&&maxx[A][i]==maxx[j][B])||(minn[A][j]==maxx[A][j]&&minn[i][B]==maxx[i][B]&&maxx[A][j]==maxx[i][B])) c2++;
			if(minn[i][A]>maxx[B][j]||minn[j][A]>maxx[B][i]) c3++;
		}
	}
	cout<<c1<<' '<<c2<<' '<<c3<<'\n';
	return 0;
}

[POI 2012] FES-Festival

差分约束跑每个联通块

计算答案加起来

posted @ 2025-08-20 10:56  gbrrain  阅读(5)  评论(0)    收藏  举报