2021.03.16集训总结

jzoj链接

2021.03.16集训总结

上午回学校体检了导致没有时间做比赛。。。

T1:给出一个长为 n n n 的0/1序列,每轮所有的01都交换位置,问T轮后的序列。

每次交换相当于1往0跳了一次,由于每个1和每个0只会跳一次,于是考虑计算出当前的1从每个0到下一个0需要等的轮数,即设 f i f_i fi跳过第 i+1 个0后,再跳过第 i 个0所需的轮数(0从前往后标号)。那么容易发现对于序列靠后的1是可以继承前面1的 f f f 的。

然后因为每次处理一个新的1时都有可能有0夹杂在中间,那么我们分几种情况讨论一下:

  • 没有新加入0时,由于需要等前面的1跳了之后才可以跳,于是将 f f f 末尾的值 + 1 +1 +1
  • 加入一个0时,恰好一轮后即可碰到前面的0,于是只需要在 f f f 的末尾添上一个 1 1 1 .
  • 加入超过一个0时,显然 f f f 的末尾需要加入相应长度的 1 1 1 ,但是考虑到这么多轮之后前面的序列也已经走了相应的这么多轮,于是可以将超过1的部分与前面超过1的 f i f_i fi 相抵消。

然后就要考虑一下求答案了,显然,答案就是要求一个最小的 x x x 使得从 x x x 开始的 f i f_i fi 之和 ≤ T \le T T,然后 x x x 到当前0的个数这么多就是这个1所能往前跳的次数,由于每次修改都至少在 f i f_i fi 的末尾 + 1 +1 +1 ,因此 x x x 是不降的,用一个指针求一下就行了。

T2:不懂

T3:交互题,没见过。。。

经过一个晚上终于搞懂了但还是没有实现。

先提出一个点做树的根,然后每次随机出一个树内的点,取其子树的点集继续递归下去,并在集合中删去该点集,做完该子树后将这个点放到一个数组 q q q 中,表示这个数组中的点还没有匹配到父亲,每次做到该子集只剩目前选定的根时,就可以找出所有 q q q 中在该子树内的点连边,因为此时的这些点肯定是该根的直系儿子。

然后就是怎么找子树内的点的问题了。

目前我们有一个点集 A A A ,一个 A A A 中的根 r t rt rt ,和一个 A A A 中的点 x x x ,要将 A A A 中是 x x x 子树内的点找出来。

考虑分治,若当前点集 A A A r t rt rt 组成的连通块中包含点 x x x ,那么就分左右两边分治下去,直到找到一个单个的点,否则退出。

贴一下T3只有6分的程序:

#include"C.hpp"
#include<bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fd(i,a,b) for(int i=a;i>=b;--i)
using namespace std;
const int N=1010;
vector<pair<int,int> > ans;
vector<int> a;
vector<int> b,c,q;
int fa[N],d[N],l[N],r[N],cnt;
bool bz[N],bz1[N];
void get(vector<int> a,int rt,bool flag){
	a.push_back(1);
	if(!ask(a,rt))return;
	a.pop_back();
	if(a.size()==1){
		int x=*a.rbegin();
		c.push_back(x);
		if(flag)bz1[x]=1;
		return;
	}
	int mid=(a.size()+1)/2;
	b.clear();
	while(a.size()>mid)b.push_back(*a.rbegin()),a.pop_back();
	get(b,rt,flag);
	get(a,rt,flag);
}
void solve(vector<int> a,int rt){
	bz[rt]=1;
	while(a.size()){
		vector<int>::iterator it=a.end();
		--it;
		int x=*it;
		while(bz[x] && it!=a.begin())x=*(--it),a.pop_back();
		a.pop_back();
		if(bz[x])break;
		c.clear();
		get(a,x,0);
		solve(c,x);
		q.push_back(x);
	}
	c.clear();
	get(q,rt,1);
	bool flag=0;
	for(vector<int>::iterator it=q.begin();it!=q.end();){
		flag=0;
		if(bz1[*it])ans.push_back(make_pair(rt,*it)),flag=1;
		if(flag)it=q.erase(it);
		else ++it;
	}
}
vector<pair<int,int> > work(int n){
	srand(time(NULL));
	fo(i,2,n)a.push_back(i);
	solve(a,1);
//	fo(o,1,100)puts("FUCK");
//	for(vector< pair<int,int> >::iterator it=ans.begin();it!=ans.end();++it){
//		printf("%d %d\n",it->first,it->second);
//	}
	return ans;
}

还有std

#include<bits/stdc++.h>
#include"C.hpp"
using namespace std;
vector<int>a,b;
vector<pair<int,int> >ans;
inline int getv(const vector<int>&a,int x){
	if(x==1)return a.empty()?-1:a.back();
	vector<int>v;
	int i,l,r,m;
	v={1};
	for(i=0;i<a.size();++i)if(a[i]!=x)v.push_back(a[i]);
	if(!ask(v,x))return -1;
	for(l=0,r=a.size()-1;l<r;){
		m=(l+r)>>1;v={1};
		for(i=0;i<=m;++i)if(a[i]!=x)v.push_back(a[i]);
		if(ask(v,x))r=m;else l=m+1;
	}
	return a[l];
}
inline void del(vector<int>&a,int x){
	int i=0;
	for(;a[i]!=x;++i);
	a.erase(a.begin()+i);
}
void solve(int x){
	int y;
	for(;;){
		y=getv(a,x);
		if(y==-1)break;
		solve(y);
	}
	for(;;){
		y=getv(b,x);
		if(y==-1)break;
		ans.push_back(make_pair(x,y));
		del(b,y);
	}
	if(x>1)del(a,x),b.push_back(x);
}
vector<pair<int,int> >work(int n){
	int i;
	for(i=2;i<=n;++i)a.push_back(i);
	solve(1);
	return ans;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值