多校冲刺省选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>
posted @ 2022-02-24 19:56  keen_z  阅读(44)  评论(0编辑  收藏  举报