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
分讨
考虑修改的边是否是原最短路

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;
}

[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
考虑对每列每行+/-某个数(相邻的行列+-相反)

发现有+x+y/-x-y不好差分约束
于是将偶数行的x取反,奇数列的y取反
变成

差分约束判断即可
[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
差分约束跑每个联通块
计算答案加起来


浙公网安备 33010602011771号