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
[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判定即可

浙公网安备 33010602011771号