2.12 模拟

前言

120pts
期望:40+100+20=160
实际:40+60+20=120
rnk 9

我yue了。
怎么又是不可抗力性挂分…
ybt的数据锅了,点编号竟然有0,splay(0)直接RE到天荒地老。
就这还有人过?
但这次这个T2做的还是挺不错的,虽然做了两个点,但是确实是一道挺难的题。
为什么分都这么高啊qwq
人强我菜。

题目解析

染色计划(color)

这个题挺妙的。
不是特别难,但做完B确实没有太多时间想这个题了。

考虑点分治。
我们每次对于分治重心,都强制让它被选取,把其颜色的点加入队列。每次弹出队首,每次往当前的父亲(即重心方向)跳,将途中未入队的颜色加入队列。如果遇到当前分治联通块以外的点,就直接放弃这次更新答案。
关于正确性:反过来想,对于最优方案,必然存在一个点分治过程中的联通块,使得最优方案的联通子树出现在一个极大的分治联通块中,且经过这个联通块。(一开始可以把这个联通块设为全集,若不过重心,就可以往一个联通块递归)

还有一个虚树的做法,但pdf讲的很草率,我也没明白…但是似乎就是对我写的那个暴力思路的一定优化。

奇度边集(edges)

大概在九点的时候看到了本题的关键性质:合法的充要条件是所有联通块的点数均为偶数
于是决定这次考试就搞它了。
这个题确实挺难的,我以为看到这个性质之后应该就呼之欲出了,结果呼了半天也没出来…
想了很多方面,由于显然的二分性,也想过整体二分,但是常规的整体二分做不了之后我就放弃了,也是因为整体二分是比较冷门的算法

LCT的做法似乎还是比较好想的。
从已经存在解的时刻(可以并查集简单求出),然后开始考虑优化答案。
最优解必然在最小生成森林上,这也是LCT的拿手好戏。
然后每次只需要尝试删去权值最大的边,这个边可以用 set 来找到。
能删去当且仅当边两侧的联通块都是偶数大小。
个人的做法比较粗暴,就是维护虚子树大小,然后暴力把边断开,看看两遍大小,如果不能断就再连回去。
由于每条边只能删一次,总复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)
然而修完数据后一交发现T成70,啊哈!
CF神机尚且给4s,ybt破机器给1s就离谱。

无奈只好看看整体二分怎么做。
(似乎主流说这个做法是sdq分治,但个人认为更像整体二分,但名字也不重要吧)
实现不是特别难,但是不太容易想到。(和LCT正好反过来
显然但重要的性质:答案单调不升。
常规的递归函数 s o l v e ( l , r , L , R ) solve(l,r,L,R) solve(l,r,L,R),表示 ( l , r ) (l,r) (l,r) 处理的询问,答案属于 [ L , R ] [L,R] [L,R]
需要保证在执行该函数时加入且仅加入了 [ 1 , l − 1 ] [1,l-1] [1,l1] 的所有边权属于 [ 1 , L ] [1,L] [1,L] 的边。
尝试求出 m i d mid mid 的答案。先把 [ l , m i d ] [l,mid] [l,mid] 边权属于 [ 1 , L ] [1,L] [1,L] 的边加入,再从 L L L 开始权值从小到大枚举边,把所有 [ 1 , m i d ] [1,mid] [1,mid] 的边加入,直到符合要求,当前边权即 a n s m i d ans_{mid} ansmid

然后就是递归处理 s o l v e ( l , m i d − 1 , a n s m i d , R ) , s o l v e ( m i d + 1 , r , L , a n s m i d ) solve(l,mid-1,ans_{mid},R),solve(mid+1,r,L,ans_{mid}) solve(l,mid1,ansmid,R),solve(mid+1,r,L,ansmid),注意依然要满足前提保证。
考虑单层复杂度是 O ( ( r − l ) + ( R − L ) ) O((r-l)+(R-L)) O((rl)+(RL)),由于递归的每层值域和询问区间的大小都是 O ( m ) O(m) O(m),所以递归树大小为 O ( m log ⁡ m ) O(m\log m) O(mlogm)
需要启发式合并带log的可撤销并查集,总复杂度 O ( m log ⁡ m log ⁡ n ) O(m\log m\log n) O(mlogmlogn)

猜拳游戏(guess)

三进制 FWT神题。
然鹅我FWT已经全忘光乐,啊哈!
好像还需要一点矩阵相关,可我线代还几乎属于知识荒漠…
直接开摆

代码

T1

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define ok debug("OK\n")
inline ll read(){
	ll x(0),f(1);char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
const int N=3e5+100;

int n,m;
int ans=2e9;

struct node{
	int to,nxt;
}p[N<<1];
int fi[N],cnt;
inline void addline(int x,int y){
	p[++cnt]=(node){y,fi[x]};fi[x]=cnt;
	return;
}
vector<int>v[N];
int hson[N],S,rt,siz[N],mx[N],vis[N],tag[N],tim,fa[N];
int col[N];
void findrt(int x,int f){
	siz[x]=1;
	tag[x]=tim;
	mx[x]=0;
	for(int i=fi[x];~i;i=p[i].nxt){
		int to=p[i].to;
		if(to==f||vis[to]) continue;
		findrt(to,x);
		siz[x]+=siz[to];
		if(mx[x]<siz[to]) mx[x]=siz[to];
	}
	mx[x]=max(mx[x],S-siz[x]);
	if(mx[x]<mx[rt]) rt=x;
	return;
}
void dfs(int x,int f){
	fa[x]=f;
	for(int i=fi[x];~i;i=p[i].nxt){
		int to=p[i].to;
		if(to==f||vis[to]) continue;
		dfs(to,x);
	}
	return;
}
int q[N],st,ed,jd[N],inque[N];
bool add(int c){
	jd[c]=tim;
	for(int x:v[c]){
		if(tag[x]!=tim) return false;
		q[++ed]=x;
		inque[x]=tim;
		//printf("  add: %d\n",x);
	}
	return true;
}
void calc(int rt){
	st=1;ed=0;
	if(!add(col[rt])) return;
	int res(0);
	while(st<=ed){
		int now=q[st++];
		if(fa[now]) now=fa[now];
		while(inque[now]!=tim){
			//printf("now=%d\n",now);
			if(jd[col[now]]!=tim){
				if(!add(col[now])) return;
				++res;
			}
			now=fa[now];
		}
	}
	ans=min(ans,res);
}
void solve(int x,int nS){
	++tim;
	S=nS;rt=0;
	findrt(x,0);
	x=rt;vis[x]=1;
	dfs(x,0);
	//printf("x=%d\n",x);
	calc(x);
	for(int i=fi[x];~i;i=p[i].nxt){
		int to=p[i].to;
		if(vis[to]) continue;
		solve(to,nS-mx[to]);
	}
	return;
}

signed main(){
	freopen("color.in","r",stdin);
	freopen("color.out","w",stdout);
	memset(fi,-1,sizeof(fi));cnt=-1;
	n=read();m=read();
	mx[0]=2e9;
	for(int i=1;i<n;i++){
		int x=read(),y=read();
		addline(x,y);addline(y,x);
	}
	for(int i=1;i<=n;i++){
		col[i]=read();v[col[i]].push_back(i);
	}
	solve(1,n);
	printf("%d\n",ans);
	return 0;
}
/*
*/

T2

LCT

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define ok debug("OK\n")
inline ll read(){
  ll x(0),f(1);char c=getchar();
  while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
  while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
  return x*f;
}
void write(ll x){
  if(x>9) write(x/10);
  putchar('0'+x%10);
}
const int N=4e5+100;

int n,m;

struct edge{
  int x,y,w,id;
  bool operator < (const edge oth)const{
    if(w!=oth.w) return w<oth.w;
    else return id<oth.id;
  }
}e[N];
set<edge>s;

int tr[N][2],f[N],rev[N],val[N],mx[N],tot,siz1[N],siz2[N];
inline bool nroot(int x){
  return tr[f[x]][0]==x||tr[f[x]][1]==x;
}
inline bool which(int x){
  return tr[f[x]][1]==x;
}
inline void pushup(int x){
  mx[x]=x;
  if(tr[x][0]){
    if(val[mx[tr[x][0]]]>val[mx[x]]) mx[x]=mx[tr[x][0]];
  }
  if(tr[x][1]){
    if(val[mx[tr[x][1]]]>val[mx[x]]) mx[x]=mx[tr[x][1]];
  }
  siz1[x]=siz1[tr[x][0]]+siz1[tr[x][1]]+siz2[x]+(x<=n);
  return;
}
inline void Rev(int x){
  if(x){
    rev[x]^=1;swap(tr[x][0],tr[x][1]);
  }
}
inline void pushdown(int x){
  if(rev[x]){
    rev[x]=0;
    Rev(tr[x][0]);Rev(tr[x][1]);
  }
}
inline void rotate(int x){
  int fa=f[x],gfa=f[fa];
  int d=which(x),son=tr[x][d^1];
  f[x]=gfa;if(nroot(fa)) tr[gfa][which(fa)]=x;
  f[fa]=x;tr[x][d^1]=fa;
  if(son){f[son]=fa;}tr[fa][d]=son;
  pushup(fa);pushup(x);
  return;
}
int zhan[N];
inline void splay(int x){
  int y(x),top(0);
  zhan[++top]=y;
  //debug("splay %d\n",x);
  while(nroot(y)) zhan[++top]=y=f[y];//debug("  %d\n",y);
  while(top) pushdown(zhan[top--]);
  for(int fa;fa=f[x],nroot(x);rotate(x)){
    if(nroot(fa)) which(fa)==which(x)?rotate(fa):rotate(x);
  }
}
inline void access(int x){
  for(int y(0);x;y=x,x=f[x]){
    splay(x);
    siz2[x]+=siz1[tr[x][1]];
    tr[x][1]=y;
    siz2[x]-=siz1[tr[x][1]];
    pushup(x);
  }
}
inline void makeroot(int x){
  access(x);splay(x);Rev(x);
}
inline int findroot(int x){
  access(x);splay(x);
  while(pushdown(x),tr[x][0]) x=tr[x][0];
  return x;
}
inline void split(int x,int y){
  makeroot(x);access(y);splay(y);
}
inline void link(int x,int y){
  //printf("link: %d %d\n",x,y);
  //assert(findroot(x)!=findroot(y));
  makeroot(x);//makeroot(y);
  access(y);splay(y);
  f[x]=y;siz2[y]+=siz1[x];
  pushup(y);
  return;
}
inline void cut(int x,int y){
  //printf("cut: %d %d\n",x,y);
  split(x,y);
  //if(tr[y][0]!=x||tr[x][1]){
  //  printf("!!\n");
  //  print();
  //  exit(0);
  //}
  //assert(tr[y][0]==x&&!tr[x][1]);
  tr[y][0]=f[x]=0;
  pushup(y);
  return;
}
inline int Siz(int x){
  makeroot(x);
  return siz1[x];
}

int fa[N],siz[N];
int find(int x){
  return fa[x]==x?x:fa[x]=find(fa[x]);
}
inline void merge(int x,int y){
  x=find(x),y=find(y);
  if(x==y) return;
  if(siz[x]>siz[y]) swap(x,y);
  fa[x]=y;siz[y]+=siz[x];
  return;
}

int calc(){
  for(int i=1;i<=n;i++) fa[i]=i,siz[i]=1;
  int num=n;
  for(auto e:s){
    int x=find(e.x),y=find(e.y);
    if(x!=y&&(siz[x]&1)&&(siz[y]&1)) num-=2;
    merge(x,y);
    //printf("  (%d %d) tot=%d\n",x,y,tot);
    if(!num) return e.w;
  }
  return -1;
}
void bf(){
  for(int i=1;i<=m;i++){
    //printf("\ni=%d\n",i);
    e[i]=(edge){(int)read(),(int)read(),(int)read(),i};
    s.insert(e[i]);
    printf("%d\n",calc());
  }
}
int nam[N],bel[N];
void solve(){
  for(int i=0;i<=n;i++) val[i]=-1;
  tot=n;
  for(int i=1;i<=n;i++){
    fa[i]=i,siz[i]=siz1[i]=1;
    mx[i]=i;
  }
  int num=n;
  for(int i=1;i<=m;i++){
    //ok;
    int x=read(),y=read(),w=read();
    
    e[i]=(edge){x,y,w,i};
    if(x!=y){
      //printf("i=%d (%d %d %d) findroot=%d %d\n",i,x,y,w,findroot(x),findroot(y));
      if(find(x)!=find(y)){
	s.insert(e[i]);
	++tot;val[tot]=w;
	link(x,tot);link(y,tot);
	nam[i]=tot;bel[tot]=i;
      }
      else{
	split(x,y);
	int o=mx[y];
	if(val[o]>w){
	  s.insert(e[i]);
	  makeroot(o);//splay(o);
	  tr[o][0]=f[tr[o][0]]=0;
	  tr[o][1]=f[tr[o][1]]=0;
	  s.erase(e[bel[o]]);
	  ++tot;val[tot]=w;
	  link(x,tot);link(y,tot);
	  nam[i]=tot;bel[tot]=i;
	}
      }
    }
    if(find(x)!=find(y)&&(siz[find(x)]&1)&&(siz[find(y)]&1)) num-=2;
    //printf("x=%d y=%d find=%d %d siz=%d %d num=%d\n",
    //x,y,find(x),find(y),siz[find(x)],siz[find(y)],num);
    merge(x,y);
 
    if(num) puts("-1");
    else{
      while(1){
	//debug("1\n");
	edge o=(*s.rbegin());
	//debug("2\n");
	int x=o.x,y=o.y,k=o.id,id=nam[k];      
	cut(id,x);cut(id,y);
	//debug("2\n");
	//printf("  k=%d (%d %d) id=%d siz=%d %d\n",k,x,y,id,Siz(x),Siz(y));
	if((Siz(x)&1)==0&&(Siz(y)&1)==0){
	  s.erase(s.find(o));
	}
	else{
	  //printf("recover\n");
	  link(id,x);link(id,y);
	  printf("%d\n",o.w);break;
	}
      }
    }
    //print();
  }
  return;
}

signed main(){
  freopen("edges.in","r",stdin);
  freopen("edges.out","w",stdout);
  
  n=read();m=read();
  if(n<=1000&&m<=3000) bf();
  else solve();
  //solve();
  return 0;
}
/*
*/

整体二分

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define ok debug("OK\n")
inline ll read(){
	ll x(0),f(1);char c=getchar();
	while(!isdigit(c)){if(c=='-') f=-1;c=getchar();}
	while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
const int N=8e5+100;
const int M=1e6+100;

int n,m,p,q;

int fa[N],siz[N],u[N],v[N],top,num;
int find(int x){
	return fa[x]==x?x:find(fa[x]);
}
inline void merge(int x,int y){
	if(!x||!y) return;
	x=find(x);y=find(y);
	if(x==y) return;
	if((siz[x]&1)&&(siz[y]&1)) num-=2;
	if(siz[x]>siz[y]) swap(x,y);
	fa[x]=y;siz[y]+=siz[x];
	++top;u[top]=x;v[top]=y;
	//printf("merge: %d %d num=%d\n",x,y,num);
	return;
}
inline void recover(int goal){
	while(top!=goal){
		int x=u[top],y=v[top];
		fa[x]=x;siz[y]-=siz[x];
		if((siz[x]&1)&&(siz[y]&1)) num+=2;
		--top;
	}
	return;
}

struct edge{
	int x,y,w,id;
}e[N],E[N];
bool cmp(edge x,edge y){
	return x.w<y.w;
}
int mx;
int ans[N];
void solve(int l,int r,int L,int R){
	if(l>r) return;
	if(L==R){
		for(int i=l;i<=r;i++) ans[i]=L;
		return;
	}
	//printf("solve: (%d %d) (%d %d)\n",l,r,L,R);
	int mid=(l+r)>>1,pre=top;
	for(int i=l;i<=mid;i++){
		if(e[i].w<=E[L].w) merge(e[i].x,e[i].y);
	}
	int o=top;
	//recover(pre);
	for(int i=L;i<=R;i++){
		if(E[i].id<=mid) merge(E[i].x,E[i].y);
		if(!num){
			ans[mid]=i;break;
		}
	}
	if(!ans[mid]) ans[mid]=m+1;
	//printf("mid=%d ans=%d\n",mid,ans[mid]);
	recover(o);
	solve(mid+1,r,L,ans[mid]);
	recover(pre);
	for(int i=L;i<=min(m,ans[mid]);i++){
		if(E[i].id<l) merge(E[i].x,E[i].y);
	}
	solve(l,mid-1,ans[mid],R);
	recover(pre);
}
signed main(){
	freopen("edges.in","r",stdin);
	freopen("edges.out","w",stdout);
	n=read();m=read();num=n;
	for(int i=1;i<=n;i++) fa[i]=i,siz[i]=1;
	for(int i=1;i<=m;i++){
		e[i]=E[i]=(edge){(int)read(),(int)read(),(int)read(),i};
		mx=max(mx,e[i].w);
		//printf("(%d %d)\n",e[i].x,e[i].y);
	}
	sort(E+1,E+1+m,cmp);
	solve(1,m,1,m+1);
	for(int i=1;i<=m;i++) printf("%d\n",ans[i]<=m?E[ans[i]].w:-1);
	return 0;
}
/*
*/

总结

没有挂分,就还是不错的。
但是今天在T2上花的时间有点过长了…
但是确实没有浪费时间,想解法,码代码,debug,对拍,过程中一直在做事。
还是要加快节奏吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值