2022NOIP A层联测15

阿哲,我把自己姓名全拼打错了(好像还不是第一次)

A

打表找规律,不会证

code
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<map>
#include<set>
#include<cctype>
#include<random>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;


int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}

const int maxn = 65539;

int n, k;
vector<int>ans1,ans2;
int op[maxn];
int main(){
	freopen("sandalphon.in","r",stdin);
	freopen("sandalphon.out","w",stdout);
	n = read(), k = read();
	if(k == n){
		printf("0\n");
		return 0;
	}
	if(n == 1){
		printf("1\n");
		printf("1 1\n");
		printf("1 2\n");
		return 0;
	}
	int mx = 1 << (n - 2);
	for(int i = 1; i <= mx; ++i)op[i] = 1;
	for(int i = n - 2; i >= 1; --i){
		for(int j = 1; j <= mx; j += (1 << i)){
			for(int k = (1 << (i - 1)); k < (1 << i); ++k)op[j + k] ^= 1;
		}
	}
	int len = 1 << (n - 1);
	for(int i = 1; i <= mx; ++i)if(op[i]){
		ans1.push_back(i * 4 - 3);
		ans1.push_back(i * 4);
		ans2.push_back(i * 4 - 1);
		ans2.push_back(i * 4 - 2);
	}else{
		ans2.push_back(i * 4 - 3);
		ans2.push_back(i * 4);
		ans1.push_back(i * 4 - 1);
		ans1.push_back(i * 4 - 2);
	}
	printf("1\n");
	printf("%d ", len);for(int v : ans1)printf("%d ",v); printf("\n");
	printf("%d ", len);for(int v : ans2)printf("%d ",v); printf("\n");
	return 0;
}

B

写个分治,感觉次数很优,具体不会分析,但是过了

后来分析大概是 nlogn 级别的,有个常数

做法考虑像线段树一样处理区间内部的连边,每次处理两个区间之间的连边

处理时对两个区间同步二分,具体来讲每次把较长区间分成两半,查询一半与较短区间的答案,通过区间间连边总数减出来另外一半的连边数,于是可以递归处理

记得充分利用线段树查询出的信息

code
#include"YuukiAsuna.h"
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<map>
#include<set>
#include<cctype>
#include<random>
#include<cassert>

using namespace std;

typedef pair<int, int> pii;

vector<pii>ans;
vector<int>S;
map<pii, int>mp;
void add(int l1, int r1, int l2, int r2, int c){
	if(c <= 0)return;
	int len1 = (r1 - l1), len2 = (r2 - l2);
	if(len1 == 0 && len2 == 0){ans.push_back(pii(l1, l2)); return;}
	if(len1 < len2){swap(l1, l2);swap(r1, r2);}
	int m1 = (l1 + r1) >> 1;
	S.clear();
	for(int i = l1; i <= m1; ++i)S.push_back(i);
	for(int i = l2; i <= r2; ++i)S.push_back(i);
	int c1 = Query(S) - mp[pii(l1, m1)] - mp[pii(l2, r2)];
	add(l1, m1, l2, r2, c1);
	add(m1 + 1, r1, l2, r2, c - c1);
}

int solve(int l, int r){
	if(l == r)return 0;
	int mid = (l + r) >> 1;
	S.clear();
	for(int i = l; i <= r; ++i)S.push_back(i);
	int c = mp[pii(l, r)] = Query(S);
	if(c){
		int cl = solve(l, mid);
		int cr = solve(mid + 1, r);
		assert(c - cl - cr >= 0);
		add(l, mid, mid + 1, r, c - cl - cr);
	}
	return c;	
}

vector<pii>Asuna(int n,int Q){
	solve(1, n);
	return ans;
}

C

发现取了若干次 cos 后函数值无限趋近于某个常数,达到题目要求的精度也只需要大概四五十次操作,于是在线段树上每个节点维护取若干次 cos 以后的区间总和,区间赋值可以直接做

这种写法需要注意常数。

考场打的就是这个,但是线段树打挂了,而且常数巨大

学习了一下小常数写法。

code
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<map>
#include<set>
#include<cctype>
#include<random>
#include<cmath>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;


const int maxn = 300005;
const double eps = 1e-6;
int n, m;
double a[maxn];
bool equ(double x, double y){return x + eps >= y && x - eps <= y;}
struct FHQ{
	#define ls t[x].l
	#define rs t[x].r
	struct node{
		int l, r, id, key, size;
		double val, tagval, sum;
		int tagcos;
		bool allsame;
	}t[maxn];
	int root, cnt;
	int New(int id, double val){
		t[++cnt].val = val;
		t[cnt].id = id;
		t[cnt].key = rand();
		t[cnt].tagval = maxn;
		t[cnt].size = 1;
		t[cnt].allsame = true;
		return cnt;
	}
	void push_up(int x){
		t[x].sum = t[ls].sum + t[rs].sum + t[x].val;
		t[x].size = t[ls].size + t[rs].size + 1;
		t[x].allsame = true;
		if(ls)t[x].allsame &= t[ls].allsame & equ(t[ls].val, t[x].val);
		if(rs)t[x].allsame &= t[rs].allsame & equ(t[rs].val, t[x].val);
	}
	void push_down(int x){
		if(t[x].tagval != maxn){
			t[ls].tagcos = t[rs].tagcos = 0;
			t[ls].tagval = t[rs].tagval = t[x].tagval;
			t[ls].val = t[rs].val = t[x].tagval;
			t[ls].sum = t[ls].size * t[ls].val;
			t[rs].sum = t[rs].size * t[rs].val;
			t[x].tagval = maxn;
		}
		if(t[x].tagcos){
			t[x].tagcos = min(t[x].tagcos, 50);
			t[ls].tagcos += t[x].tagcos;
			t[rs].tagcos += t[x].tagcos;
			for(int i = 1; i <= t[x].tagcos; ++i)t[ls].val = cos(t[ls].val), t[rs].val = cos(t[rs].val);
			t[ls].sum = t[ls].size * t[ls].val;
			t[rs].sum = t[rs].size * t[rs].val;
			t[x].tagcos = 0;
		}
	}
	void split(int x, int &l, int &r, int id){
		if(!x)return l = r = 0, void();
		push_down(x);
		if(id < t[x].id){split(t[x].l, l, t[x].l, id); r = x;}
		else {split(t[x].r, t[x].r, r, id); l = x;}
		push_up(x); return;
	}
	int merge(int x, int y){
		if(!x || !y)return x | y;
		if(t[x].key < t[y].key){push_down(x), t[x].r = merge(t[x].r, y), push_up(x); return x;}
		else{push_down(y), t[y].l = merge(x, t[y].l), push_up(y); return y;}
	}
	void ins(int id, double val){root = merge(root, New(id, val));}
	void modify(int L, int R, double v){
		int l = 0, m = 0, r = 0;
		split(root, l, r, R + 1);
		split(l, l, m, L);
		t[m].tagval = t[m].val = v;
		t[m].tagcos = 0;
		t[m].sum = t[m].size * v;
		t[m].allsame = true;
		root = merge(merge(l , m), r);
	}
	void dfs(int x){
		if(t[x].allsame){
			++t[x].tagcos;
			t[x].val = cos(t[x].val);
			t[x].sum = t[x].val * t[x].size;
			return;
		}
		push_down(x);
		if(ls)dfs(ls);
		if(rs)dfs(rs);
		t[x].val = cos(t[x].val);
		push_up(x);
	}
	void modify_cos(int L, int R){
		int l = 0, m = 0, r = 0;
		split(root, l, r, R + 1);
		split(l, l, m, L);
		dfs(m);
		root = merge(merge(l , m), r);
	}
	double query(int L, int R){
		int l = 0, m = 0, r = 0;
		split(root, l, r, R + 1);
		split(l, l, m, L);
		double ans = t[m].sum;
		root = merge(merge(l , m), r);
		return ans;
	}
}t;
int main(){
	// freopen("excalibur.in","r",stdin);
	// freopen("excalibur.out","w",stdout);
	scanf("%d%d",&n,&m);
	for(int i = 1; i <= n; ++i)scanf("%lf",&a[i]);
	for(int i = 1; i <= n; ++i)t.ins(i, a[i]), 	printf("%lf\n",t.query(1, n));
	for(int i = 1; i <= m; ++i){
		printf("%lf\n",t.query(1, n));
		int op, l, r; scanf("%d%d%d",&op,&l,&r);
		if(op == 1)t.modify_cos(l, r);
		if(op == 2)printf("%.10lf\n",t.query(l, r));
		if(op == 3){
			double v; scanf("%lf",&v);
			t.modify(l, r, v);
		}
	}
	return 0;
}

D

又双叒叕是褐的

考虑按位确定答案,每次 check ans 的某一位是否能取 1

然后一个结论就是最优答案是匹配,不会证

调用函数 calc(x,y,dep,lim)

表示匹配 x, y 子树,到了 dep 位,限制的答案下界为 lim

返回最少多少点无法匹配

calc(root,root,maxdep,lim)返回为 0 时,当前答案就是可行的

考虑如何进行匹配

  1. limdep 位为 1

那么只能 10 配对,递归处理两棵子树加起来返回

  1. limdep 位为 0

如果 x0<=y1x1<=y0 那么 10 配对一定符合答案,返回 sizeysizex

不存在的话,记 c0=x0+y0 c1=x1+y1

c0<=c1, 那么让 x1,y1配对,返回无法配对的 w

如果 w>=c0 wc0 为一个下界,否则可取消部分配对,与c0 匹配一定合法,所以与 0max

另外的下界是 abs(sizexsizey)

所以直接对他们取max即可

code
// ubsan: undefined
// accoders
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<map>
#include<set>
#include<cctype>
#include<random>
#include<iostream>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;


int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}

const int maxn = 500005;

int n;
int tot, size[maxn * 32], son[maxn * 32][2];
int root = 1, cnt = 1; 
void insert(int x){
	int now = root;
	for(int j = 30; j >= 0; --j){
		++size[now];
		int k = (x >> j) & 1;
		if(!son[now][k])son[now][k] = ++cnt;
		now = son[now][k];
	}
	++size[now];
}
int calc(int x, int y, int dep, int lim){
	if(!x || !y)return size[x] | size[y];
	if(dep < 0)return abs(size[x] - size[y]);
	if((lim >> dep) & 1)return calc(son[x][0], son[y][1], dep - 1, lim) + calc(son[x][1], son[y][0], dep - 1, lim);
	else{
		if(size[son[x][0]] <= size[son[y][1]] && size[son[x][1]] <= size[son[y][0]])return size[y] - size[x];
		if(size[son[x][0]] >= size[son[y][1]] && size[son[x][1]] >= size[son[y][0]])return size[x] - size[y];
		int c0 = size[son[x][0]] + size[son[y][0]], c1 = size[son[x][1]] + size[son[y][1]];
		if(c0 < c1){
			int w = calc(son[x][1], son[y][1], dep - 1, lim);
			return max(abs(size[x] - size[y]), w - c0);
		}else{
			int w = calc(son[x][0], son[y][0], dep - 1, lim);
			return max(abs(size[x] - size[y]), w - c1);	
		}
	}
}
int main(){
	freopen("win.in","r",stdin);
	freopen("win.out","w",stdout);
	n = read();
	for(int i = 1; i <= n; ++i)insert(read());
	int ans = 0;
	for(int i = 30; i >= 0; --i){
		if(!calc(1, 1, 30, ans + (1 << i)))ans += 1 << i;
	}
	printf("%d\n", ans);
	return 0;
}

最近某Delov越来越爱fake了。

posted @   Chen_jr  阅读(12)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示