[硫化铂]签到题

Posted StaroForgin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[硫化铂]签到题相关的知识,希望对你有一定的参考价值。

签到题

题目描述


题解

我连签到都签不了…

首先如果只看原题面的话很容易联想到最近做的最小割树的题,显然可以建出一棵最小割树出来,但那样是铁定会 T T T飞的,我们需要继续观察一下此题的性质。
这道题最关键的一个点是所有点的度数 ⩽ 3 \\leqslant 3 3,这也就说明我们的任意两个点间的最大流都不会超过 3 3 3
不超过 3 3 3也就意味着我们两个点间的最大流只有 0 , 1 , 2 , 3 0,1,2,3 0,1,2,3 4 4 4种可能。
最大流为 0 0 0也就是不连通的情况,可以通过并查集预处理出来。
最大流为 1 1 1也就是通过一条边联通,这种情况下这两个点显然是属于不同的边双连通分量的,我们也可以通过 t a r j a n tarjan tarjan预处理。

所以我们现在需要的是对于每个边双连通分量,找到我们通过切断其中的两条边,可以使得哪些点对不连通,除去这个,剩下的就都是最大流为 3 3 3的了。
我们可以考虑先将 d f s dfs dfs树建出来,在 d f s dfs dfs树上切断两条边,能够将树分成两块。
显然,我们不可能切两条非树边,切开照样是棵树,仍是连通的,那我们就只剩两种情况:

  • 切一条树边与一条非树边,这种情况切掉树边 ( u , v ) (u,v) (u,v)后肯定 v v v的子树中肯定有且仅有一条边连到 v v v的联通块中。如果没有的话就该是两个边双联通分量了,如果有两条我们切完仍是连通的。这种情况也就是说我们能够将这棵树从这里划分成两块。
  • 切两条树边,这种情况下我们切的两条树边一定是祖孙关系,这两条树边之间的部分没有能够超越第一条树边的返祖边,它的返祖边来自第二条树边的下方,现在与之割掉自然也就不联通了。这样的话我们相当于将整棵树分成两条树边之间与之外的两块。

容易发现,当且仅当两个点在所有的划分方案中都在同一个块中,它们之间的最大流才是 3 3 3,否则一定存在一种方案可以割两条边将它们划开。
我们可以将其看作一个等价类,同一个等价类中的点的最大流是 3 3 3

考虑怎么维护这个等价类。
我们发现我们每次划分的块其 d f s dfs dfs序都是几乎连续的,只会有很少的几个区间。
那我们不妨就在这个 d f s dfs dfs区间上异或一个很大的数,来 H a s h Hash Hash它这个等价类,显然 H a s h Hash Hash值不同的两个点属于不同的等价类。
第一种情况我们只需记录一下从子树中传到祖先的返祖边的数量,这个树上差分就行,如果只有一条,就可以给这个子树的区间异或上一个数。
第二种情况可以转化成跨越这两条树边的返祖边集合相同,那么这两条边就可以被割开划分整棵树。
跨越这两条边的返祖边集合相同好判断,我们只需要用上面的做法在树上差分用 H a s h Hash Hash维护就可以了。
但集合相同的树边其实是可能很多的,我们不可能没两条边之间都处理一下,但我们事实上只需要处理它祖先中离它最近的一条,因为对于 ( x , y , z ) ( x < y < z ) (x,y,z)(x<y<z) (x,y,z)(x<y<z),如果我们划分 ( x , z ) (x,z) (x,z)显然是不敌我们先划分 ( x , y ) (x,y) (x,y)再划分 ( y , z ) (y,z) (y,z)这相当于将一个等价类分成了两个不同的等价类,显然限制是更强的,所以我们处理这些就行了。
我们可以按 d f s dfs dfs序的方式再次遍历这棵树,记录下它前面离它最近的 H a s h Hash Hash值相同的边时哪条边,划分一下就可以。
最后只用统计一下每个等价类的大小,就可以贡献到答案上了。
不同等价类之间是 2 2 2的贡献,同一个等价类中是 3 3 3的贡献。

由于我们的点达到了 1 0 6 10^6 106的级别,所以我们每次异或上的 H a s h Hash Hash值得很大,反正 i n t int int一类我好像过不了。
然后就很卡常了,居然还不能用 m a p map map,我还得手打一个 H a s h M a p HashMap HashMap
时间复杂度 O ( n ) O\\left(n\\right) O(n),最开始不用并查集维护,直接 d f s dfs dfs遍历也可以。

源码

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#include<bits/stdc++.h>
using namespace std;
#define MAXN 1000005
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
#define lson (rt<<1)
#define rson (rt<<1|1)
typedef long long LL;
typedef unsigned long long uLL; 
typedef long double ld;
typedef pair<int,int> pii;
const int INF=0x3f3f3f3f;
const int mo=998244353;
const int mod=1e6+3;
const int inv2=499122177;
const int jzm=2333;
const int zero=2000;
const int n1=2000;
const int orG=3,ivG=332748118;
const long double Pi=acos(-1.0);
const double eps=1e-12;
template<typename _T>
_T Fabs(_T x)return x<0?-x:x;
char gc()static char buf[20000000],*p1=buf,*p2=buf;return p1==p2&&(p2=(p1=buf)+fread(buf,1,20000000,stdin),p1==p2)?EOF:*p1++;
#define getchar gc
template<typename _T>
void read(_T &x)
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0')if(s=='-')f=-1;s=getchar();
	while('0'<=s&&s<='9')x=(x<<3)+(x<<1)+(s^48);s=getchar();
	x*=f;

template<typename _T>
void print(_T x)if(x<0)x=(~x)+1;putchar('-');if(x>9)print(x/10);putchar(x%10+'0');
int gcd(int a,int b)return !b?a:gcd(b,a%b);
int add(int x,int y,int p)return x+y<p?x+y:x+y-p;
void Add(int &x,int y,int p)x=add(x,y,p);
int qkpow(int a,int s,int p)int t=1;while(s)if(s&1)t=1ll*t*a%p;a=1ll*a*a%p;s>>=1;return t;
int n,m,dfn[MAXN],low[MAXN],belong[MAXN],idx,cnt,sz[MAXN],lw[MAXN];
int sta[MAXN],stak,ord[MAXN],fa[MAXN],rd[MAXN],pre[MAXN],dep[MAXN];
LL sp[MAXN],dif[MAXN];
bool vis[MAXN],vp[MAXN];
LL ans,num;bool insta[MAXN];
vector<int>vec[MAXN],G[MAXN],P[MAXN];
mt19937 e(time(NULL));
uniform_int_distribution<LL> g(1LL,(1LL<<62)-1LL);
struct HashMap
	int sum[MAXN],nxt[MAXN],head[MAXN],sta[MAXN],stak,tot;LL val[MAXN];
	void insert(LL ai,int aw)
		int pos=ai%mod,now=head[pos];if(!head[pos])sta[++stak]=pos;
		while(now&&val[now]!=ai)now=nxt[now];
		if(!now)now=++tot,val[now]=ai,nxt[now]=head[pos],head[pos]=now;
		sum[now]=aw;
	
	int query(LL ai)
		int pos=ai%mod,now=head[pos];
		while(now&&val[now]!=ai)now=nxt[now];
		return sum[now];
	
	void clear()
		for(int i=1;i<=tot;i++)sum[i]=nxt[i]=val[i]=0;tot=0;
		while(stak)head[sta[stak--]]=0;
	
mp;
void makeSet(int x)for(int i=1;i<=x;i++)fa[i]=i;
int findSet(int x)return fa[x]==x?x:fa[x]=findSet(fa[x]);
void unionSet(int a,int b)int u=findSet(a),v=findSet(b);if(u^v)fa[u]=v;
void tarjan(int u,int fa)
	dfn[u]=low[u]=++idx;sta[++stak]=u;insta[u]=1;
	int siz=G[u].size();
	for(int i=0;i<siz;i++)
		int v=G[u][i];if(v==fa)continue;
		if(!dfn[v])tarjan(v,u),low[u]=min(low[u],low[v]);
		else if(insta[v])low[u]=min(low[u],dfn[v]);
	
	if(dfn[u]==low[u])cnt++;int v;dov=sta[stak--];belong[v]=cnt;insta[v]=0;while(u^v);

void dosaka1(int u,int fa)
	int siz=P[u].size();vis[u]=1;
	dep[u]=dep[fa]+1;dfn[u]=++idx[硫化铂]旅行

[硫化铂]守序划分问题

[硫化铂]传染

[硫化铂]密码

[硫化铂]启程的日子

[硫化铂]黑白树