pyyzDay17

[NOIP2022] 建造军营

从部分分入手

缩点+超级大炮

[ZJOI2004] 嗅探器

将割点条件改一改即可

#include<bits/stdc++.h>
using namespace std;
#define int long long
struct Node{
	int nex,to;
}e[5000005];
int low[5000005],dfn[5000005],cc[5000005],A,B;
int n,m,x,y,h[5000005],sum=0,fa,vis[5000005];
int cnt=1,ccc;
void add(int x,int y){
	cnt++;
	e[cnt]={h[x],y};
	h[x]=cnt;
}
void Tarjan(int x){
	dfn[x]=low[x]=++sum;
	for(int i=h[x];i;i=e[i].nex){
		int v=e[i].to;
		if(!dfn[v]){
			Tarjan(v);
			low[x]=min(low[x],low[v]);
			if(low[v]>=dfn[x]&&x!=A&&dfn[v]<=dfn[B]) vis[++ccc]=x;
		}
		else{
			low[x]=min(low[x],dfn[v]);
		}
	}
}
signed main(){
	cin>>n;
	while(true){
		cin>>x>>y;
		if(x==0&&y==0) break;
		add(x,y);
		add(y,x);
	}
	cin>>A>>B;
	Tarjan(A);
	if(!ccc){
		cout<<"No solution"<<'\n';
		return 0;
	}
	sort(vis+1,vis+ccc+1);
	for(int i=1;i<=ccc;i++){
		if(vis[i]!=A&&vis[i]!=B){
			cout<<vis[i]<<'\n';
			return 0;
		}
	}
	cout<<"No solution"<<'\n';
	return 0;
}

圆方树

每个点双建一个方点

向圆点建边

发现一定是棵树

且圆点一定连方点

两个圆点之间的点双数量=经过方点数量

道路相遇

统计两个点之间的圆点数量即可

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define int long long
#define ull unsigned long long
#define pii pair<int,int>
#define pdi pair<double,int>
#define fr first
#define sc second
#define mkp make_pair
#define gtc getchar
#define ptc putchar
#define dbug puts("owo");
#define pb push_back
ll read(){
    ll x=0,f=1;char ch=gtc();
    while(ch<48||ch>57) {if(ch=='-') f=-1; ch=gtc();}
    while(ch>=48&&ch<=57) x=x*10+ch-48,ch=gtc();
    return x*f;
}
void write(ll x){
    if(x<0) x=-x,ptc('-');
    if(x>9) write(x/10);
    ptc(x%10+'0');
}
inline void printsp(ll x){write(x),ptc(' ');}
inline void println(ll x){write(x),ptc('\n');}
const int maxn=1e6+5,mod=1e9+7;
int n,m;
vector<int> g[maxn],k[maxn];
int dfn[maxn],low[maxn],now=0,cnt=0;
stack<int> st;
int f[maxn][25],dep[maxn];

void tar(int nd){
    st.push(nd);
    dfn[nd]=low[nd]=++now;
    for(auto u:g[nd]){
        if(!dfn[u]){
            tar(u);
            low[nd]=min(low[nd],low[u]);
            if(low[u]>=dfn[nd]){
                ++cnt;    
                while(st.top()!=u){
                    k[st.top()].pb(cnt);
                    k[cnt].pb(st.top());
                    st.pop();
                }
                k[cnt].pb(u),k[u].pb(cnt),st.pop();
                k[cnt].pb(nd),k[nd].pb(cnt);
            }
        }else{
            low[nd]=min(low[nd],dfn[u]);
        }
    }
}

int get_lca(int p1,int p2){
    if(dep[p1]<dep[p2]) swap(p1,p2);
    for(int i=20;i>=0;i--)
        if(dep[f[p1][i]]>=dep[p2])
            p1=f[p1][i];
    if(p1==p2) return p1;
    for(int i=20;i>=0;i--)
        if(f[p1][i]!=f[p2][i])
            p1=f[p1][i],p2=f[p2][i];
    return f[p1][0];
}

void dfs(int nd,int fa){
    f[nd][0]=fa,dep[nd]=dep[fa]+1;
    for(int i=1;i<=20;i++) f[nd][i]=f[f[nd][i-1]][i-1];
    for(auto u:k[nd]){
        if(u==fa) continue;
        dfs(u,nd);
    }
}
void solve(){
    n=read(),m=read();
    for(int i=1;i<=m;i++){
        int u=read(),v=read();
        g[u].push_back(v);
        g[v].push_back(u);
    }
    cnt=n;
    tar(1);
    dfs(1,0);
    int Q=read();
    while(Q--){
        int u=read(),v=read();
        int lca=get_lca(u,v);
        println((dep[u]+dep[v]-2*dep[lca])/2+1);
    }
}
signed main(){
    solve();
    return 0;
}

[SDOI2018] 战略游戏

加强版

对于点集S,计算相邻的答案

最后/2即可

KNIGHTS - Knights of the Round Table

不憎恨的骑士互相连边

判断是否有骑士不在有奇环的点双之间即可

Tourists

建圆方树

方点记录点双最小代价信息

割点必走

【模板】静态仙人掌

建圆方树

如果是由原点指向方点的,边权设为0

否则边权设为那个原点到这个方点父亲的最短距离

查询x和y的距离,若x和y的lca是圆点,在树上的距离就是原图的距离

否则倍增,跳到父亲不是lca的位置找到连在环上的那两个点,然后算一下在环上的最短距离即可

给你个仙人掌,每次询问给你若干点,问你有多少个点对,满足他们之间的任意路径都全经过这些关键点

建圆方树

分讨k>=1的情况

如果说k等于1,答案是 size[x]*(n-size[x]+1)

如果k>1,且关键点无法被一条路径覆盖,答案是0

如果k>1,我们找到最远的两个点,若他们不是祖孙关系,答案是Size[x]*size[y]

如果是祖孙关系的话,设x是祖先,y是子孙,a是x的儿子,b是y的父亲,那么答案是 (n-size[a])*size[b]

判断关键点能否被一条路径覆盖,可以按dfn序排序,找lca,路径一定是先上后下

[NWRRC 2017] Grand Test

找点双+一条边即可

2-SAT

【模板】2-SAT

[POI 2001] 和平委员会

模板

#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 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;
}
void out(int x){
	if(x<0)putchar('-'),x=-x;
	if(x<10)putchar(x+'0');
	else out(x/10),putchar(x%10+'0');
}
int dfn[2000005],low[2000005],cnt,col,belong[2000005],vis[2000005],st[2000005],top,vi[2000005];
vector<int> tu[2000005];
void dfs(int x){
	dfn[x]=low[x]=++cnt;
	vis[x]=1;
	st[++top]=x;
	for(auto ed:tu[x]){
		if(dfn[ed]==0){
			dfs(ed);
			low[x]=min(low[ed],low[x]);
		}
		else if(vis[ed]==1){
			low[x]=min(low[x],dfn[ed]);
		}
	}
	if(low[x]==dfn[x]){
		int y=-1;
		col++;
		while(1){
			y=st[top--];
			vis[y]=0;
			belong[y]=col;
			if(x==y) break;
		}
	}
}
signed main()
{
	//freopen("filename.in", "r", stdin);
	//freopen("filename.out", "w", stdout);
	int n=read(),m=read();
	for(int i=1;i<=m;i++){
		int u=read(),v=read();
		if(v%2==0) tu[u].push_back(v-1);
		else tu[u].push_back(v+1);
		if(u%2==0) tu[v].push_back(u-1);
		else tu[v].push_back(u+1);
	}
//	for(int i=1;i<=n*2;i+=2){
//		for(auto ed:tu[i]){
//			cout<<ed<<' ';
//		}
//		cout<<'\n';
//	}
	for(int i=1;i<=n*2;i++){
		if(!dfn[i]){
			dfs(i);
		}
	}
	for(int i=1;i<=n*2;i+=2){
		if(belong[i]==belong[i+1]){
			cout<<"NIE"<<'\n';
			return 0;
		}
	}
	for(int i=1;i<=n*2;i+=2){
		if(belong[i]<belong[i+1]) cout<<i<<' ';
		else cout<<i+1<<' ';
	}
	cout<<'\n';
	return 0;
}

[JSOI2010] 满汉全席

拆条件

板子

#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 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;
}
void out(int x){
	if(x<0)putchar('-'),x=-x;
	if(x<10)putchar(x+'0');
	else out(x/10),putchar(x%10+'0');
}
int dfn[2000005],low[2000005],cnt,col,belong[2000005],vis[2000005],st[2000005],top;
vector<int> tu[2000005];
void dfs(int x){
	dfn[x]=low[x]=++cnt;
	vis[x]=1;
	st[++top]=x;
	for(auto ed:tu[x]){
		if(dfn[ed]==0){
			dfs(ed);
			low[x]=min(low[ed],low[x]);
		}
		else if(vis[ed]==1){
			low[x]=min(low[x],dfn[ed]);
		}
	}
	if(low[x]==dfn[x]){
		int y=-1;
		col++;
		while(1){
			y=st[top--];
			vis[y]=0;
			belong[y]=col;
			if(x==y) break;
		}
	}
}
void solve(){
	int n=read(),m=read();
	for(int i=1;i<=n*2;i++){
		tu[i].clear();
		vis[i]=low[i]=dfn[i]=belong[i]=0;
	} 
	cnt=col=top=0;
	for(int i=1;i<=m;i++){
		char a,b;
		int u,v;
		cin>>a>>u>>b>>v;
		int fl=0,ff=0;
		if(a=='m') fl=n;
		if(b=='m') ff=n;
		tu[u+n-fl].push_back(v+ff);
		tu[v+n-ff].push_back(u+fl);
	}
	for(int i=1;i<=n*2;i++){
		if(!dfn[i]){
			dfs(i);
		}
	}
	for(int i=1;i<=n;i++){
		if(belong[i]==belong[i+n]){
			cout<<"BAD"<<'\n';
			return ;
		}
	}
	cout<<"GOOD"<<'\n';
}
signed main()
{
	//freopen("filename.in", "r", stdin);
	//freopen("filename.out", "w", stdout);
	int T=read();
	while(T--){
		solve();
	}
	return 0;
}

[NOI2017] 游戏

对于每个图ABC,发现只可选两个种类的车

对于x,暴力判断x=A还是B即可

不必判断x=C,因为ABC已经都尝试过一遍了

#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 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;
}
void out(int x){
	if(x<0)putchar('-'),x=-x;
	if(x<10)putchar(x+'0');
	else out(x/10),putchar(x%10+'0');
}
int dfn[200005],low[200005],cnt,col,belong[200005],vis[200005],st[200005],top;
vector<int> tu[200005];
int X[200005],cn;
int id[200005],iid[200005];
char di[200005],dii[200005];
void dfs(int x){
	dfn[x]=low[x]=++cnt;
	vis[x]=1;
	st[++top]=x;
	for(auto ed:tu[x]){
		if(dfn[ed]==0){
			dfs(ed);
			low[x]=min(low[ed],low[x]);
		}
		else if(vis[ed]==1){
			low[x]=min(low[x],dfn[ed]);
		}
	}
	if(low[x]==dfn[x]){
		int y=-1;
		col++;
		while(1){
			y=st[top--];
			vis[y]=0;
			belong[y]=col;
			if(x==y) break;
		}
	}
}
signed main()
{
	//freopen("filename.in", "r", stdin);
	//freopen("filename.out", "w", stdout);
	int n=read(),d=read();
	string s,t;
	cin>>t;
	s=" ";
	s+=t;
	for(int i=1;i<=n;i++){
		if(s[i]=='x'){
			X[i]=++cn;
		}
	}
	int m=read();
	for(int i=1;i<=m;i++){
		id[i]=read();
		cin>>di[i];
		iid[i]=read();
		cin>>dii[i];
	}
	int maxx=(1<<d)-1;
	for(int i=0;i<=maxx;i++){
		for(int j=1;j<=n*2;j++){
			tu[j].clear();
			vis[j]=low[j]=dfn[j]=belong[j]=0;
		} 
		cnt=col=top=0;
		for(int j=1;j<=m;j++){
			int ii=id[j];
			int hi=di[j]-'A'+1;
			int jj=iid[j];
			int hj=dii[j]-'A'+1;
			char hii=s[ii];
			char hjj=s[jj];
			if(hii=='x'){
				if(i>>(X[ii]-1)&1) hii='a';
				else hii='b';
			}
			if(hjj=='x'){
				if(i>>(X[jj]-1)&1) hjj='a';
				else hjj='b';
			}
//			cout<<hii<<' '<<hjj<<'\n';
			int u=0,v=0;
			if(hii=='a'){
				if(hi==2) u=ii;
				if(hi==3) u=ii+n;
			}
			if(hii=='b'){
				if(hi==1) u=ii;
				if(hi==3) u=ii+n;
			}
			if(hii=='c'){
				if(hi==1) u=ii;
				if(hi==2) u=ii+n;
			}
			if(hjj=='a'){
				if(hj==2) v=jj;
				if(hj==3) v=jj+n;
			}
			if(hjj=='b'){
				if(hj==1) v=jj;
				if(hj==3) v=jj+n;
			}
			if(hjj=='c'){
				if(hj==1) v=jj;
				if(hj==2) v=jj+n;
			}
			int uu=0,vv=0;
			if(u<=n) uu=u+n;
			else uu=u-n;
			if(v<=n) vv=v+n;
			else vv=v-n;
//			cout<<u<<' '<<v<<' '<<'\n';
			if(u){
				if(!v){
					tu[u].push_back(uu);
				}
				else{
					tu[u].push_back(v);
					tu[vv].push_back(uu);
				}
			}
		}
		for(int j=1;j<=n*2;j++){
			if(!dfn[j]){
				dfs(j);
			}
		}
//		cout<<i<<111<<'\n';
//		for(int j=1;j<=2*n;j++){
//			cout<<belong[j]<<'\n';
//		}
		int f=0;
		for(int j=1;j<=n;j++){
			if(belong[j]==belong[j+n]){
				f=1;
				break;
			}
		}
		if(f) continue;
		for(int j=1;j<=n;j++){
			char hjj=s[j];
			if(hjj=='x'){
				if(i>>(X[j]-1)&1) hjj='a';
				else hjj='b';
			}
			if(hjj=='a'){
				if(belong[j]<belong[j+n]) cout<<'B';
				else cout<<'C';
			}
			if(hjj=='b'){
				if(belong[j]<belong[j+n]) cout<<'A';
				else cout<<'C';
			}
			if(hjj=='c'){
				if(belong[j]<belong[j+n]) cout<<'A';
				else cout<<'B';
			}
		}
		cout<<'\n';
		return 0;
	}
	cout<<"-1"<<'\n';
	return 0;
}

[PA 2010] Riddle

对于每个关键点,选了周围的不能选,建边

对于每条边,u不选,v一定选,建边

线段树/前缀优化

Radio Stations

把边的关系转成前缀建边关系

设X为交集中任意一个数字

假如我们选了一个[l,r]的电站,那么表示X在[1,r]内,不在[1,l-1]内

同样,如果X不在[1,r]内,[l,r]这个电站不可选

如果X在[1,l-1]内,也不可选

我们可以另外开一些点表示X在不在[1,i]内,然后前缀优化建图即可

Duff in Mafia

二分

对于第一个限制,选出的边集无交点,那不就是表示,对于每个点来说,它的邻边最多选出来一个

对于第二个限制,余下的同颜色的边无交点,那不就是说明,对于每个点周围相同颜色的边,最多有一条不被选出来

前缀优化建边

寻找罪犯

我们考虑每个人是否是罪犯和每个证词是否是真的

如果一个证词为真/假,可以推导出它供出的人的真/假,这里需要同时对其逆否命题连边

如果一个人说了假话,那么它是罪犯

如果一个人不是罪犯,那么他说的就全是真话

注意到一个犯人最多说一句假话,那么如果一个犯人的第i个证词为假,那么前i+1个,和第i+1个往后的就全是真话

这一部分可以用前缀优化建图处理

用2-sat判定即可

posted @ 2025-08-22 10:28  gbrrain  阅读(3)  评论(0)    收藏  举报