NOI.ac2020省选模拟赛10

比赛链接

A.ball

problem

有n条轨道,在这些纵道之间有m条横道,当有球从某个轨道经过时,如果路过了一个横道,那么这个球就会通过横道到达相邻的一个纵道。

先放入m个横道,然后每次拿走一个。然后会有一些询问,询问当前从第i个轨道放下球最终会滚到哪个位置。

solution

\(a_i\)表示第i个球滚到的位置。

如果没有横道那么答案就是\(a_i=i\)。发现从上到下每个横道的作用就是交换两个相邻球的答案。也就是说对于一个在\(i\)\(i+1\)的横道,在经过这个横道时候\(a_i\)\(a_{i+1}\)会交换。

可是置换不满足交换律。所以我们并不能先算出答案在拿掉横道之后更新答案。

发现一个在时间i拿走的横道\(t_i\)只会对\(t_i\)之前的询问产生左右。所以我们就开n个平衡树,第i个平衡树表示答案为i的询问,里面存放的是询问的时间。

然后从上到下处理每个横道,记第i个横道被拿走的时间为\(tim_i\),交换的位置是\((x,x+1)\)。那么就把x的平衡树里和x+1的平衡树里,时间点小于\(tim_i\)的询问交换。

code

/*
* @Author: wxyww
* @Date:   2020-06-10 09:01:29
* @Last Modified time: 2020-06-10 09:26:50
*/
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<ctime>
using namespace std;
typedef long long ll;
const int N = 2000010;
#define ls TR[cur].ch[0]
#define rs TR[cur].ch[1]
ll read() {
	ll x=0,f=1;char c=getchar();
	while(c<'0'||c>'9') {
		if(c=='-') f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9') {
		x=x*10+c-'0';
		c=getchar();
	}
	return x*f;
}
struct node {
	int pre,ch[2],val;
}TR[N];
int tot,root[N];
int getwh(int cur) {
	return TR[TR[cur].pre].ch[1] == cur;
}
void rotate(int cur) {
	int fa = TR[cur].pre,gr = TR[fa].pre,f = getwh(cur);
	TR[cur].pre = gr;TR[gr].ch[getwh(fa)] = cur;
	TR[TR[cur].ch[f ^ 1]].pre = fa;TR[fa].ch[f] = TR[cur].ch[f ^ 1];
	TR[fa].pre = cur;TR[cur].ch[f ^ 1] = fa;
}
void splay(int &rt,int cur,int to) {
	while(TR[cur].pre != to) {
		if(TR[TR[cur].pre].pre != to) {
			if(getwh(TR[cur].pre) == getwh(cur)) rotate(TR[cur].pre);
			else rotate(cur);
		}
		rotate(cur);
	}
	if(!to) rt = cur;
}
void ins(int &rt,int cur,int val,int fa) {
	if(!cur) {
		cur = ++tot;
		if(fa) TR[fa].ch[val > TR[fa].val] = cur;
		TR[cur].val = val;
		TR[cur].pre = fa;
		splay(rt,cur,0);
		return;
	}
	if(val < TR[cur].val) ins(rt,ls,val,cur);
	else ins(rt,rs,val,cur);
}

int lst(int cur,int val) {//val的后继 
//	printf("%d %d\n",val,TR[cur].val);
	int now = 1e9,ret = 0;
	while(cur) {
//		printf("%d\n",cur);
		if(TR[cur].val < val) cur = rs;
		else {
			if(TR[cur].val < now) now = TR[cur].val,ret = cur;
			cur = ls;
		}
	}
	return ret;
	
}

int debug(int cur,int x) {
	if(TR[cur].val > x) {
//		cerr<<TR[cur].val<<" "<<x<<endl;
		return 1;
	}
	int tag = 0;
	if(ls) tag |= debug(ls,x);
	if(rs) tag |= debug(rs,x);
	return tag;
}

void solve(int l,int r,int x) {
	int rt1 = lst(root[l],x),rt2 = lst(root[r],x);
	
	splay(root[l],rt1,0);
	
	splay(root[r],rt2,0);

//	if(debug(TR[root[l]].ch[0],TR[root[l]].val)) cerr<<l<<endl;
//	if(debug(TR[root[r]].ch[0],TR[root[r]].val)) cerr<<r<<endl;

	swap(TR[root[l]].ch[0],TR[root[r]].ch[0]);
	
	if(TR[TR[root[r]].ch[0]].val > TR[root[r]].val) cerr<<"!!!"<<endl;
	TR[TR[root[l]].ch[0]].pre = root[l];
	TR[TR[root[r]].ch[0]].pre = root[r];
}

int t[N],tim[N],ans[N];
void dfs(int cur,int x) {
	if(!cur) return;
	ans[TR[cur].val] = x;
	dfs(ls,x);dfs(rs,x);
}
void dfss(int cur) {
	if(!cur) return;
	dfss(ls);
	cerr<<cur<<" ";
	dfss(rs);
}
int main() {

//	freopen("1.in","r",stdin);
//	freopen("bug.in","r",stdin);
//	freopen("A.out","w",stdout);
	int n = read(),m = read();

	for(int i = 1;i <= m;++i) {
		t[i] = read();
	}
	
	int Q = read();
	
	for(int i = 1;i <= n;++i)
		ins(root[i],root[i],Q + 1,0);
	
	for(int i = 1;i <= m;++i) tim[i] = Q + 1;
	
	for(int i = 1;i <= Q;++i) {
		int opt = read();
		if(opt == 1) {
			int x = read();
			
			tim[x] = i;
		}
		else {
			int x = read();
			ins(root[x],root[x],i,0);
		}
	}	

	for(int i = 1;i <= m;++i)
		solve(t[i],t[i] + 1,tim[i]);

	for(int i = 1;i <= n;++i) dfs(root[i],i);
//	dfss(root[165]);
	for(int i = 1;i <= Q;++i)
		if(ans[i]) printf("%d\n",ans[i]);

	return 0;
}

B.C

problem

给出一个\(n\)个点,\(m\)条边的无向图,记第\(i\)个点的度数为\(d_i\)。删去一些边,使得剩下的边数不超过\(\lceil\frac{n+m}{2}\rceil\)。切删边后第\(i\)个点的度数\(f_i\)要满足\(f_i\ge \lceil\frac{d_i}{2}\rceil\)

solution

一种不知道对不对的贪心方法,将每条边按照较小度数的端点的度数从大到小排序。然后优先删前面的,能删就删。

code

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#include<ctime>
#include<set>
#include<cstring>
#include<string>
using namespace std;
typedef long long ll;
const int N = 1000010;
ll read() {
	ll x = 0,f = 1;char c = getchar();
	while(c < '0' || c > '9') {
		if(c == '-') f = -1;
		c = getchar();
	}
	while(c >= '0' && c <= '9') {
		x = x * 10 + c - '0';
		c = getchar();
	}
	return x * f;
}
struct node {
	int u,v,id;
}e[N];
int du[N];
bool cmp(const node &A,const node &B) {
	return min(du[A.u],du[A.v]) > min(du[B.u],du[B.v]);
}
int ans[N];
int main() {
	int n = read(),m = read();
	for(int i = 1;i <= m;++i) {
		e[i].u = read();e[i].v = read();
		du[e[i].u]++;du[e[i].v]++;
		e[i].id = i;
	}
	for(int i = 1;i <= n;++i)
		du[i] = (du[i] >> 1);
	
	sort(e + 1,e + m + 1,cmp);
		
	for(int i = 1;i <= m;++i) {
		if(du[e[i].u] && du[e[i].v]) {
			du[e[i].u]--;du[e[i].v]--;
		}
		else ans[e[i].id] = 1;
	}
	for(int i = 1;i <= m;++i) printf("%d ",ans[i]);
	
	return 0;
}

C.寄蒜几盒

problem

有一个\(n\)\(n\)为偶数)条边的凸多边形。选择每对相对的边,然后将他们延长,这样将平面分为几部分,然后把凸多边形所在的平面染色。

每次询问一个点,回答这个点是不是被染色。强制在线。

solution

将每个点看做障碍,如果一个点不能被染色,当且仅当他能看到至少\(\frac{n}{2}\)条边。验证一下发现这个结论是对的,怎么想出来的我也不知道。。

然后我们先以\(0\)号边和\(n/2\)号边中能看到的为起点,然后二分出一个能看到的与不能看到的交界的位置。从这个位置在判断一下是不是能看到\(n/2\)条边就行了。

问题在于如何判断一个点能不能看到一条边。因为凸多边形上的点都是逆时针给出的。一个点能看到一条边\((i,i+1)\),当且仅当与点\(i\)之间的连边斜率小于与点\(i+1\)之间的斜率。

code

/*
* @Author: wxyww
* @Date:   2020-06-11 07:25:11
* @Last Modified time: 2020-06-11 19:32:55
*/
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<ctime>
#include<cmath>
using namespace std;
typedef long long ll;
const int N = 100010;
#define int ll
ll read() {
	ll x = 0,f = 1;char c = getchar();
	while(c < '0' || c > '9') {
		if(c == '-') f = -1; c = getchar();
	}
	while(c >= '0' && c <= '9') {
		x = x * 10 + c - '0'; c = getchar();
	}
	return x * f;
}
ll x[N],y[N];
bool check(ll x1,ll y1,ll x2,ll y2,ll x3,ll y3) {
	return (__int128)(y1 - y3) * (x2 - x3) >= (__int128)(y2 - y3) * (x1 - x3);
}
signed main() {

	ll T = read(),cnt = 0,n = read();
	for(int i = 0;i < n;++i) x[i] = read(),y[i] = read();
	int Q = read();
	while(Q--) {
		ll A = read(),B = read();
		if(T) {
			A ^= cnt * cnt * cnt;
			B ^= cnt * cnt * cnt;
		}

		int t1 = check(x[0],y[0],x[1],y[1],A,B);
		int t2 = check(x[n / 2],y[n / 2],x[(n / 2 + 1) % n],y[(n / 2 + 1) % n],A,B);

		if(t1 && t2) {
			puts("NE");continue;
		}
		if(!t1 && !t2) {
			puts("DA");cnt++;continue;
		}
		// printf("%d %d\n",t1,t2);
		int k = 0;
		if(t2) k = n / 2;

		int l = 0,r = n / 2;
		int ret = 0;
		while(l <= r) {
			int mid = (l + r) >> 1;
			if(check(x[(k + mid) % n],y[(k + mid) % n],x[(k + mid + 1) % n],y[(k + mid + 1) % n],A,B)) ret = k + mid,l = mid + 1;
			else r = mid - 1;
		}
		ret %= n;
		// printf("%lld %lld\n",x[ret],y[ret]);
		// cout<<ret<<endl;
		if(check(x[(ret - n / 2 + n + 1) % n],y[(ret - n / 2 + n + 1) % n],x[(ret - n / 2 + 2 + n) % n],y[(ret - n / 2 + 2 + n) % n],A,B)) {
			puts("NE");
		}
		else {
			cnt++;puts("DA");
		}
	}

	return 0;
}
posted @ 2020-06-12 07:39  wxyww  阅读(9)  评论(0编辑  收藏  举报