多校冲刺省选20[2022.2.23]
T1 定位系统
不会QAQ
T2 签到题
注意到每个点度数最大为 \(3\) ,因此任意点对的最小割最大为 \(3\) 。考虑对每种取值分别求出点对数。
\(0\) 和 \(1\) 的点对数分别可以求联通块和求割边求,考虑怎么求 \(2\) 和 \(3\) 的点对。求出一个,剩下的就可以减出来了。
对每个边双分别考虑。每种割两条边使图不联通的方案中,割出的两个连通块之间的点对最小割一定为 \(2\) 。如果有点对在所有方案中都没被割开,则它们的最小割为 \(3\) 。把一直没被割开的集合看作一个等价类,于则求 \(3\) 的方案数等价于求这些等价类的大小。
对边双整出 DFS 树,那么割边的方案分为两条树边和一条树边一条非树边。
两条树边必须保证它们互为祖孙关系,且跨过它们的非树边集合相同。一条树边一条非树边必须保证树边只被这一条非树边跨过。
可以对非树边随机赋值,树边权值为跨过它们的非树边权值异或和,然后就可做了。
\(code:\)
T2
#include<bits/stdc++.h>
#include<bits/extc++.h>
#define x first
#define y second
#define int long long
using namespace std;
namespace IO{
typedef pair<int,int> PII;
typedef unsigned long long ULL;
typedef double DB; typedef long long LL;
int read(){
int x=0,f=0; char ch=getchar();
while(ch<'0'||ch>'9'){ f|=ch=='-'; ch=getchar(); }
while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
return f?-x:x;
} char outp[50];
void write(int x,char sp,int len=0){
if(x<0) putchar('-'), x=-x;
do{ outp[len++]=(x%10)^48; x/=10; }while(x);
for(int i=len-1;~i;i--) putchar(outp[i]); putchar(sp);
}
void ckmin(int& x,int y){ x=x<y?x:y; }
void ckmax(int& x,int y){ x=x>y?x:y; }
} using namespace IO;
const int NN=1500010;
int n,m,ans,tot;
namespace DSU{
int ff[NN],szb[NN];
void init(){ for(int i=1;i<=n;i++) ff[i]=i, szb[i]=1; }
int getf(int x){ return x==ff[x]?x:ff[x]=getf(ff[x]); }
void merge(int x,int y){
x=getf(x); y=getf(y);
if(x==y) return;
if(szb[x]<szb[y]) swap(x,y);
ff[y]=x; szb[x]+=szb[y];
}
} using namespace DSU;
namespace Graph{
int top,dcc,dfc,stk[NN],low[NN],dfn[NN],bel[NN],szd[NN],pnt[NN];
bool vis[NN],cut[NN];
vector<PII>e[NN];
void tarjan(int u,int in){
vis[stk[++top]=u]=1; dfn[u]=low[u]=++dfc;
for(PII v:e[u]) if(v.y^in)
if(!dfn[v.x]){
tarjan(v.x,v.y); ckmin(low[u],low[v.x]);
if(low[v.x]==dfn[v.x]) cut[v.y]=1;
} else if(vis[v.x]) ckmin(low[u],dfn[v.x]);
vis[stk[top--]]=0;
}
void colour(int u,int c){
++szd[bel[pnt[c]=u]=c];
for(PII v:e[u]) if(!cut[v.y]&&!bel[v.x])
colour(v.x,c);
}
void buildcc(){
for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i,0);
for(int i=1;i<=n;i++) if(!bel[i]) colour(i,++dcc);
}
} using namespace Graph;
namespace DccOperation{
mt19937_64 zxsty((ULL)(new int));
__gnu_pbds::gp_hash_table<ULL,int>pos,buc;
ULL all,div[NN],evl[NN],tag[NN];
int top,dep[NN],stk[NN];
void firdfs(int u,int p){
vis[u]=1; stk[++top]=u; dep[u]=dep[p]+1;
for(PII v:e[u]) if(!cut[v.y]&&v.x!=p)
if(!vis[v.x]) firdfs(v.x,u), evl[v.y]^=div[v.x], div[u]^=div[v.x];
else if(dep[v.x]<dep[u]) pos[evl[v.y]=zxsty()]=v.y, div[v.x]^=evl[v.y], div[u]^=evl[v.y];
}
void secdfs(int u,int p){
vis[u]=1;
for(PII v:e[u]) if(!cut[v.y]&&!vis[v.x]&&v.x!=p){
if(pos.find(evl[v.y])!=pos.end()){
int x=zxsty(),y=zxsty();
all^=x; tag[v.x]^=x^y;
}
stk[++top]=v.y; secdfs(v.x,u);
}
}
void thidfs(int u,int p){
vis[u]=1;
for(PII v:e[u]) if(!cut[v.y]&&!vis[v.x]&&v.x!=p){
int tmp=pos[evl[v.y]],x=zxsty(),y=zxsty();
if(tmp) all^=x, tag[tmp]^=x^y, tag[v.x]^=x^y;
pos[evl[v.y]]=v.x; thidfs(v.x,u); pos[evl[v.y]]=tmp;
}
}
void pushtag(int u,int p,ULL now){
++buc[(now^=tag[u])]; vis[u]=1;
for(PII v:e[u]) if(!cut[v.y]&&!vis[v.x]&&v.x!=p)
pushtag(v.x,u,now);
}
void clear(){ for(int i=1;i<=top;i++) vis[stk[i]]=0; }
void solve(int u){
pos.clear(); buc.clear(); all=top=0;
firdfs(u,0); clear(); secdfs(u,0); clear(); // solve the contribution from one TreeEdge & one Non-TreeEdge
pos.clear(); thidfs(u,0); clear(); // solve the contribution from two TreeEdges
pushtag(u,0,all);
for(PII v:buc) ans+=v.y*(v.y-1)/2*3,tot-=v.y*(v.y-1)/2;
}
} using namespace DccOperation;
signed main(){
freopen("juice.in","r",stdin);
freopen("juice.out","w",stdout);
n=read(); m=read(); init();
for(int a,b,i=1;i<=m;i++){
a=read(); b=read(); merge(a,b);
e[a].push_back({b,i});
e[b].push_back({a,i});
}
buildcc(); memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++) if(getf(i)==i) tot+=szb[i]*(n-szb[i]);
for(int now,i=1;i<=dcc;i++){
now=szd[i]*(szb[getf(pnt[i])]-szd[i]);
tot+=now; ans+=now;
}
tot=n*(n-1)-tot>>1; ans>>=1;
for(int i=1;i<=dcc;i++) solve(pnt[i]);
write(ans+tot*2,'\n');
return 0;
}
T3 卷王
给的数值只可能是直径端点或 \(-1\) 。
特判掉 \(n=1\) 。
\(|S|=1\) 时造个菊花就行。
\(|S|=2\) 时,若集合内有 \(-1\) ,设值为 \(x\) 的点为 \(y\) ,就构造 \(x\to -1 (\to -1) \to y\) ,多的点在链底造菊花。否则造两条数值相同,长度相等的链,同样链底造菊花,再把两条链接起来。
\(|S|=3\) 时同上,在两条链中间加个 \(-1\) 的菊花。
\(code:\)
T3
#include<bits/stdc++.h>
#define yes puts("Possible")
#define no puts("Impossible"), exit(0)
using namespace std;
namespace IO{
typedef unsigned long long ULL;
typedef double DB; typedef long long LL;
int read(){
int x=0,f=0; char ch=getchar();
while(ch<'0'||ch>'9'){ f|=ch=='-'; ch=getchar(); }
while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
return f?-x:x;
} char outp[50];
void write(int x,char sp,int len=0){
if(x<0) putchar('-'), x=-x;
do{ outp[len++]=(x%10)^48; x/=10; }while(x);
for(int i=len-1;~i;i--) putchar(outp[i]); putchar(sp);
}
void ckmin(int& x,int y){ x=x<y?x:y; }
void ckmax(int& x,int y){ x=x>y?x:y; }
} using namespace IO;
const int NN=1000010;
int n,p[NN];
void print(int a,int b){ write(a,' '); write(b,'\n'); }
namespace SubTasks{
int p1,p2,p3,n1;
set<int>dif,s[NN];
void task1(){
if(dif.size()^1) return;
if(n<=3||!s[n1].size()) no; yes;
for(int i=2;i<=n;i++) print(1,i);
}
void task2(){
if(dif.size()^2) return;
for(int i=1;i<=n;i++) if(~p[i]) p1=p[i];
if(s[n1].size()<2||s[p1].size()<2||p[p1]!=-1) no;
s[n1].erase(p1);
if(s[n1].size()>1&&s[p1].size()<3) no;
yes; print(p1,*s[n1].begin());
if(s[n1].size()==1) print(*s[n1].begin(),*s[p1].begin());
else{
print(*s[n1].begin(),*s[n1].rbegin());
for(int i:s[n1]) if(i!=(*s[n1].begin())&&i!=(*s[n1].rbegin())) print(i,*s[n1].rbegin());
print(*s[p1].begin(),*s[n1].rbegin());
}
for(int i:s[p1]) if(i^(*s[p1].begin())) print(i,*s[p1].begin());
}
void task3(){
if(dif.size()^3) return;
for(int i=1;i<=n;i++) if(~p[i])
if(!p1) p1=p[i];
else if(p[i]^p1) p2=p[i];
if(p[p1]!=p2||p[p2]!=p1) no;
if(s[p1].size()==1||s[p2].size()==1){
if(s[p1].size()^1) swap(p1,p2);
if(s[p2].size()>1) no;
if(n==2&&!s[n1].size()) yes, print(p1,p2), exit(0);
if(n==3&&s[n1].size()) yes, print(p1,*s[n1].begin()), print(p2,*s[n1].begin()), exit(0);
no;
}
if((s[p1].size()<3&&s[p2].size()>2)||(s[p1].size()>2&&s[p2].size()<3)) no;
s[p1].erase(p2); s[p2].erase(p1); yes;
if(s[n1].size()){
print(*s[p1].rbegin(),*s[n1].begin()); print(*s[p2].rbegin(),*s[n1].begin());
for(int i:s[n1]) if(i^(*s[n1].rbegin())) print(i,*s[n1].rbegin());
} else print(*s[p1].rbegin(),*s[p2].rbegin());
print(p1,*s[p2].begin()); print(p2,*s[p1].begin());
if(s[p1].size()>1&&s[p2].size()>1){
print(*s[p1].begin(),*s[p1].rbegin());
print(*s[p2].begin(),*s[p2].rbegin());
for(int i:s[p1]) if(i!=(*s[p1].begin())&&i!=(*s[p1].rbegin())) print(i,*s[p1].rbegin());
for(int i:s[p2]) if(i!=(*s[p2].begin())&&i!=(*s[p2].rbegin())) print(i,*s[p2].rbegin());
}
}
void task4(){ if(dif.size()>3) no; }
} using namespace SubTasks;
signed main(){
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
n=read(); dif.insert(-1); n1=n+1;
for(int i=1;i<=n;i++) dif.insert(p[i]=read()), s[p[i]==-1?n1:p[i]].insert(i);
if(n==1){ puts(p[1]==1?"Possible":"Impossible"); exit(0); }
task1(); task2(); task3(); task4();
return 0;
}
</details>