2022 牛客多校第六場 & 第八場題解

2022 牛客多校第六場 & 第八場題解

Forest

依照 Kruskal,對每條邊計算讓兩個點不連通的方案數。容斥+精密實現即可通過。

Fourier and Theory for the Universe

一眼 min_25,然後沒寫。。。後來分析一通,發現確實 min_25 用 dfs 實現的時候將這些東西都記錄下來了。。。

From AtCoder

若存在一個 n 階排列的和 \(<0\),那麼一定沒解。否則可以通過以下構造得到答案:

假設主對角線是最大匹配的排列,那麼執行 \((n,i,a(i,i))\),使得 \(a(i,i)=0(1\le i<n),a(n,n)\ge 0\)

之後便是約束 \(x_i-x_j+a(i,j)\ge 0\),直接上差分約束即可。

這裏先處理 \(a(i,i)\) (應該是?)差分約束無法處理這些點。

那麼問題就變成求這個排列了,直接 KM 即可。(昨天去學了一波,挺妙的算法)

Hash

亂搞,分組,儘量將能同餘的數量變大。注意數據隨機。

Line

直接暴力將所有的點在該方向放 \(d-1\) 個點即可。注意不能共線,可以找幾個大質數爲 \(base\) 多隨幾次,或者直接上 \(31\)\(37\)。(玄學)

SolarPea and Inversion

Striking String Problem

題解


這場是人做的嗎.jpg

Lexicographic Comparison

隊友和我一起想,我寫的題。

首先,\(A\) 數組其實沒有太大用,你只需要判斷兩個序列第一個不一樣在哪個位置,再把 \(A\) 比較一下即可。

一次變換相當於在一個循環中移位,發現當循環節長度 \(\%\ (y-x)=0\) 就會相等。那麼只需找到循環節長度模後不等於 \(0\) 的第一個位置。

那麼就可以進行分類了:

若循環節長度 \(\le 64\),我們對所有這樣長度的都開一棵平衡樹,維護位置的最小值。

若循環節長度 \(>64\),開個 \(set\) 維護一下,直接暴力詢問所有的循環節即可。

現在有個 \(P\) 數組的 swap 操作,發現是一個開環和縮環的過程,直接拿非旋 \(treap\ split\ merge\) 一下即可。代碼細節有億點點多,好久沒寫過這麼長的代碼了

點擊查看代碼
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=100005;
int n,m,B,a[maxn],p[maxn];
int ch[maxn][2],fa[maxn],key[maxn],val[maxn],siz[maxn],mn[maxn];
int rt[maxn],sz,seed=233;
set<int> s[maxn];
vector<int> A;
inline int Rand() { return seed=(int)(451312391341ll*seed%2147483647); }
inline int newnode(int v)
{
	key[++sz]=Rand();
	val[sz]=mn[sz]=v,siz[sz]=1;
	ch[sz][0]=ch[sz][1]=fa[sz]=0;
	return sz;
}
inline void pushup(int x)
{
	siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+1;
	mn[x]=min(min(mn[ch[x][0]],mn[ch[x][1]]),val[x]);
	if(ch[x][0]) fa[ch[x][0]]=x;
	if(ch[x][1]) fa[ch[x][1]]=x;
	fa[x]=0;
}
void split(int x,int &a,int &b,int k)
{
	if(!x) { a=b=0; return; }
	if(siz[ch[x][0]]<k) a=x,split(ch[x][1],ch[a][1],b,k-siz[ch[x][0]]-1);
	else b=x,split(ch[x][0],a,ch[b][0],k);
	pushup(x);
}
void merge(int &x,int a,int b)
{
	if(!a || !b) { x=a+b; return; }
	if(key[a]<key[b]) x=a,merge(ch[x][1],ch[a][1],b);
	else x=b,merge(ch[x][0],a,ch[b][0]);
	pushup(x);
}
inline int getid(int x)
{
	while(fa[x]) x=fa[x];
	return mn[x];
}
inline int getrnk(int x)
{
//	puts("getrnk");
	int k=siz[ch[x][0]]+1;
	while(fa[x])
	{
		if(x==ch[fa[x]][1])
			k+=siz[ch[fa[x]][0]]+1;
		x=fa[x];
	}
//	puts("fin.");
	return k;
}
inline int getkth(int x,int k)
{
//	printf("getkth %d %d\n",x,k);
	while(k>0)
	{
		if(k==siz[ch[x][0]]+1) return val[x];
		if(k<=siz[ch[x][0]]) x=ch[x][0];
		else k-=siz[ch[x][0]]+1,x=ch[x][1];
	}
	return -1;
}
inline void erase(int x)
{
	vector<int>::iterator it;
	for(it=A.begin();it!=A.end();it++)
		if(*it==x) { A.erase(it); break; }
}
inline void del(int x)
{
	if(siz[x]<=B) s[siz[x]].erase(mn[x]);
	else erase(x);
}
inline void ins(int x)
{
	if(siz[x]<=B) s[siz[x]].insert(mn[x]);
	else A.push_back(x);
}
void print(int x)
{
	if(ch[x][0]) print(ch[x][0]);
	printf("%d ",val[x]);
	if(ch[x][1]) print(ch[x][1]);
}
inline void check(int &rt)
{
	int t=mn[rt],k=getrnk(t),a,b;
	split(rt,a,b,k-1);
	merge(rt,b,a);
}
inline void S()
{
	for(int i=1;i<=B;i++)
		if(!s[i].empty())
		{
			set<int>::iterator it;
			printf("len=%d: ",i);
			for(it=s[i].begin();it!=s[i].end();it++)
				printf("%d ",*it);
			putchar('\n');
		}
}
int main()
{
//	freopen("data.in","r",stdin);
//	freopen("czx.out","w",stdout);
	mn[0]=0x3f3f3f3f;
	int T;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d",&n,&m); B=32;
		for(int i=1;i<=n;i++) p[i]=a[i]=i,rt[i]=newnode(i),s[1].insert(i);
		char op[10]; ll x,y; int u,v,p,q,c,d,pos,_rt;
		for(int Case=1;Case<=m;Case++)
		{
//			printf("Case %d\n",Case);
			scanf("%s%lld%lld",op,&x,&y);
//			if(x>y) swap(x,y);
			if(op[0]=='s' && op[5]=='a') swap(a[x],a[y]);
			else if(op[0]=='s' && op[5]=='p')
			{
				if(x==y) continue;
//				puts("OK!");
				u=getid(x),v=getid(y);
//				puts("OK!");
				if(u!=v)
				{
					del(rt[u]),del(rt[v]);
					p=getrnk(x),q=getrnk(y);
//					if(Case==8)
//					{
//						printf("x=%d y=%d\n",x,y);
//						printf("u=%d v=%d\n",u,v);
//						printf("p=%d q=%d\n",p,q);
//					}
					split(rt[u],rt[u],c,p);
					split(rt[v],rt[v],d,q);
					merge(rt[u],rt[u],d);
					merge(rt[u],rt[u],rt[v]);
					merge(rt[u],rt[u],c);
					rt[v]=0;
					_rt=rt[u],rt[u]=0,rt[mn[_rt]]=_rt;
					ins(_rt);
				}
				else
				{
					del(rt[u]);
					p=getrnk(x),q=getrnk(y);
					if(p>q) swap(p,q),swap(x,y);
//					printf("%d %d %d %d\n",p,q,x,y);
					split(rt[u],rt[u],c,q);
					split(rt[u],rt[u],d,p);
					merge(rt[u],rt[u],c);
//					printf("siz %d %d\n",siz[rt[u]],siz[d]);
					_rt=rt[u],rt[u]=0,rt[mn[_rt]]=_rt,ins(_rt);
					rt[mn[d]]=d,ins(d);
//					printf("siz %d %d\n",siz[_rt],siz[d]);
				}
			}
			else
			{
				if(x==y) { puts("="); continue; }
				x--,y--;
				pos=n+1;
//				puts("OK!");
				for(int i=1;i<=B;i++)
					if(!s[i].empty() && (y-x)%i!=0)
						pos=min(pos,*s[i].begin());
//				puts("OK!");
				for(auto i:A)
					if((y-x)%siz[i]!=0)
						pos=min(pos,mn[i]);
				if(pos==n+1) { puts("="); continue; }
//				printf("%d\n",pos);
				u=getid(pos),v=getrnk(pos);
//				printf("%d %d\n",u,v);
//				printf("%d %d\n",x,y);
//				printf("kth %d %d\n",(v-1+x)%siz[rt[u]]+1,(v-1+y)%siz[rt[u]]+1);
				p=getkth(rt[u],(v-1+x)%siz[rt[u]]+1);
				q=getkth(rt[u],(v-1+y)%siz[rt[u]]+1);
//				printf("%d %d\n",p,q);
				puts((a[p]<a[q])?"<":">");
			}
//			for(int i=1;i<=n;i++)
//				if(fa[i]==0)
//				{
//					printf("tree %d: %d\n",i,siz[i]);
//					print(i); putchar('\n');
//				}
//			S();
//			for(int i=1;i<=n;i++) printf("%d ",rt[i]); putchar('\n');
//			printf("V: ");
//			for(int i=0;i<A.size();i++) printf("%d ",A[i]); putchar('\n');
		}
		for(int i=1;i<=B;i++) s[i].clear();
		A.clear(),sz=0,seed=233;
	}
	return 0;
}

Equivalence in Connectivity

賽後 10min AC,寄

先想 \(\log^3n\) 怎麼做。可以通過樹剖,將樹的問題轉化成序列問題,再線段樹分治+哈希即可。哈希可以直接異或實現。

同時,對於同一條邊來說,我們發現一次 add 和 remove 就是對子樹 dfs 序的一段區間進行 \(+1\) 或者 \(-1\),然後將區間的值 \(=1\) 的加入線段樹中。那麼這樣連續的區間最多會有 \(O(|S|)\) 個,考慮在 map 上差分求出這些區間,可以降下一個 log。

時間 \(O(n\log^2 n)\),可以通過此題。

點擊查看代碼
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
#define fi first
#define se second
const int maxn=400005;
ull seed=23333333333,base=19260817,t=998244353;
inline ull Rand() { return seed=seed*base+t; }
struct Union_Set
{
    int fa[maxn],cnt[maxn],tot;
    pii e[10000005]; ull p[maxn],ans;
    inline int find(int x)
    {
        while(x!=fa[x]) x=fa[x];
        return x;
    }
    inline int merge(int x,int y)
    {
//      printf("merge %d %d\n",x,y);
        int u=find(x),v=find(y);
        if(u==v) return 0;
        if(cnt[u]>cnt[v]) swap(u,v);
        fa[u]=v,cnt[v]+=cnt[u];
        ans^=p[u]^p[v],p[v]+=p[u],ans^=p[v];
        e[++tot]=pii(u,v);
        return 1;
    }
    inline void cancel()
    {
        int u=e[tot].fi,v=e[tot].se; tot--;
//      printf("cancel %d %d\n",u,v);
        fa[u]=u,cnt[v]-=cnt[u];
        ans^=p[v],p[v]-=p[u],ans^=p[u]^p[v];
    }
    inline void reset(int cancel_cnt)
    {
        while(cancel_cnt--) cancel();
    }
}s;
int k,n,m,x[maxn],y[maxn],p[maxn],x_[maxn],y_[maxn],st[maxn],ed[maxn],MP[maxn],tim;
int head[maxn],to[maxn<<1],nxt[maxn<<1],tot; char op[maxn][10];
inline void add_edge(int x,int y) { to[++tot]=y,nxt[tot]=head[x],head[x]=tot; }
void dfs(int x,int ff)
{
    st[x]=++tim,MP[tim]=x;
    for(int i=head[x];i;i=nxt[i])
    {
        int y=to[i];
        if(y==ff) continue;
        dfs(y,x);
    }
    ed[x]=tim;
}
vector<pii> E[maxn<<2];
#define ls (rt<<1)
#define rs (rt<<1|1)
void update(int rt,int l,int r,int x,int y,int u,int v)
{
//  printf("update %d %d %d %d\n",x,y,u,v);
    if(x>y) return;
    if(x<=l && r<=y) { E[rt].push_back(pii(u,v)); return; }
    int mid=(l+r)>>1;
    if(x<=mid) update(ls,l,mid,x,y,u,v);
    if(y>mid) update(rs,mid+1,r,x,y,u,v);
}
struct node { int x,y,l,r,op; } q[maxn<<1]; int cnt;
inline bool operator < (const node &a,const node &b)
{
    if(a.x!=b.x) return a.x<b.x;
    if(a.y!=b.y) return a.y<b.y;
    if(a.l!=b.l) return a.l<b.l;
    if(a.r!=b.r) return a.r<b.r;
    return a.op<b.op;
}
map<ull,int> mp;
vector<int> v[maxn];
int ha_cnt;
void calc(int rt,int l,int r)
{
//  printf("calc %d %d %d\n",rt,l,r);
    int cancel_cnt=0;
    for(auto it:E[rt])
        cancel_cnt+=s.merge(it.fi,it.se);
    if(l==r)
    {
        if(!mp[s.ans]) mp[s.ans]=++ha_cnt;
        v[mp[s.ans]].push_back(MP[l]);
        s.reset(cancel_cnt);
        return;
    }
    int mid=(l+r)>>1;
    calc(ls,l,mid);
    calc(rs,mid+1,r);
    s.reset(cancel_cnt);
}
map<int,int> tr;
int main()
{
//  freopen("data.in","r",stdin);
//  freopen("czx.out","w",stdout);
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d",&k,&n,&m); s.ans=0;
        for(int i=1;i<=n;i++) s.p[i]=Rand(),s.ans^=s.p[i],s.fa[i]=i,s.cnt[i]=1;
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&x[i],&y[i]);
            if(x[i]>y[i]) swap(x[i],y[i]);
            q[++cnt]=(node){x[i],y[i],1,k,0};
        }
        for(int i=2;i<=k;i++)
        {
            scanf("%d%s%d%d",&p[i],op[i],&x_[i],&y_[i]),add_edge(p[i],i);
            if(x_[i]>y_[i]) swap(x_[i],y_[i]);
        }
        dfs(1,0);
//      for(int i=2;i<=k;i++) printf("%d %d\n",p[i],i);
//      for(int i=1;i<=k;i++) printf("%d %d\n",st[i],ed[i]);
        for(int i=2;i<=k;i++)
            q[++cnt]=(node){x_[i],y_[i],st[i],ed[i],(op[i][0]=='a')?0:1};
        sort(q+1,q+cnt+1);
        for(int l=1,r=0;l<=cnt;l=r+1)
        {
            r=l;
            for(;r<cnt && q[r].x==q[r+1].x && q[r].y==q[r+1].y;r++);
//          printf("(%d,%d)\n",q[l].x,q[l].y);
//          for(int i=l;i<=r;i++)
//              printf("%d %d %d\n",q[i].l,q[i].r,q[i].op);
            tr.clear();
            for(int i=l;i<=r;i++)
                if(q[i].op==0) tr[q[i].l]++,tr[q[i].r+1]--;
                else tr[q[i].l]--,tr[q[i].r+1]++;
            int cur=0,las=-1;
            for(auto o:tr)
            {
                if(las!=-1 && cur>0)
                    update(1,1,k,las,o.fi-1,q[l].x,q[l].y);
                cur+=o.se,las=o.fi;
            }
        }
//      puts("OK!");
        calc(1,1,k);
        printf("%d\n",ha_cnt);
        for(int i=1;i<=ha_cnt;i++)
        {
            printf("%d ",(int)v[i].size());
            sort(v[i].begin(),v[i].end());
            for(int j=0;j<(int)v[i].size();j++)
                printf("%d ",v[i][j]);
            putchar('\n');
        }
        for(int i=1;i<=k;i++) head[i]=0;
        for(int i=1;i<=ha_cnt;i++) v[i].clear();
        for(int i=1;i<=(k<<2);i++) E[i].clear();
        tim=tot=cnt=ha_cnt=s.tot=0,mp.clear();
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章