2022NOIP A层联测12

A. 染色

考虑距离为质数的不能同色,于是距离为 \(2 , 3 ..\)

等等,唯一的偶质数为 \(2\), 于是按照 \(\mod 4\)分组即可

需要特判 \(n <= 6\)

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

int n;


void sol(){
	printf("4\n");
	for(int i = 1; i <= n; ++i)printf("%d ",(i - 1) % 4 + 1);
}
void pr1(){
	printf("1\n");
	for(int i = 1; i <= n; ++i)printf("1 ");
}
void pr2(){
	printf("2\n");
	for(int i = 1; i <= n; ++i)printf("%d ", (i + 1) / 2);
}
void pr3(){
	printf("3\n");
	if(n == 5)printf("1 2 2 3 1\n");
	else printf("1 2 3 3 1 2\n");
}
int main(){
	freopen("color.in","r",stdin);
	freopen("color.out","w",stdout);
	cin >> n;
	if(n >= 7)sol();
	else{
		if(n == 1 || n == 2)pr1();
		if(n == 3 || n == 4)pr2();
		if(n == 5 || n == 6)pr3();
	}
	return 0;
}

B. 序列

考场猜了个半真不假的结论,最后忘了 \(n\) 的限制对剩余值的限制,,挂了。。。

首先肯定排序不等式,然后最小值大概需要确定,确定了最小值,那么我让根比较小的 \(a\) 配对的 \(b\) 变大一定最优

但是看起来不像二分,猜测是单峰函数,写对拍确实拍出来单峰,虽然不严格

于是搓了个假三分,感觉切了,还在对拍

于是我暴力写假了,两个假代码,拍了个寂寞

不过按照题解的说法,函数是个多峰的?三分好像是假的,不会不会,溜了

我还是太菜了

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

ll read(){
	ll 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;
const int inf = 0x3f3f3f3f;
ll mi, sum, n, m, k, d;
ll a[maxn];
ll ckans(ll mx){
	ll res = d - mx * sum;
	ll dt = 0;
	for(int i = 1; i <= m && res; ++i){
		ll d1 = min(n - mx, res / a[i]);
		dt += d1; res -= d1 * a[i];
	}
	res = dt + mx * m + k * mx;
	return res;
}

void sol(){
	mi = inf, sum = 0;
	n = read(), m = read(), k = read(), d = read();
	for(int i = 1; i <= m; ++i)a[i] = read();
	sort(a + 1, a + m + 1);
	for(int i = 1; i <= m; ++i){mi = min(mi, a[i]); sum += a[i];}
	ll r = n, l = 0, ans = 0;
	while(l <= r){
		if(r - l <= 10){
			for(ll i = l; i <= r; ++i)ans = max(ans, ckans(i));
			break;
		}
		ll lmid = (r - l) / 3 + l;
		ll rmid = r - (r - l) / 3;
		ll al = ckans(lmid), ar = ckans(rmid);
		ans = max(ans, max(al, ar));
		if(al < ar)l = lmid + 1;
		else r = rmid - 1;
	}
	printf("%lld\n",ans);
}

int main(){
	freopen("array.in","r",stdin);
	freopen("array.out","w",stdout);
	int t = read();
	for(int i = 1; i <= t; ++i)sol();
	return 0;
}

C. 树上询问

比较套路的题,把询问转化为与深度相关的形式,于是自然的想法是主席树

我差点就写了

冷静过后发现,这题又没有强制在线,我直接树上差分,开桶记录答案就行了

如果在线的话,可能就得打主席树了

code
#include<bits/stdc++.h>

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, m, rt1[maxn], rt2[maxn];
int fa[maxn], top[maxn], dep[maxn], son[maxn], size[maxn];
int head[maxn], tot;
struct edge{int to, net;}e[maxn << 1 | 1];
void add(int u, int v){
	e[++tot].net = head[u];
	head[u] = tot;
	e[tot].to = v;
}

void dfs1(int x){
	size[x] = 1;
	for(int i = head[x]; i; i = e[i].net){
		int v = e[i].to;
		if(v == fa[x])continue;
		dep[v] = dep[x] + 1; fa[v] = x;
		dfs1(v);
		size[x] += size[v];
		son[x] = size[son[x]] >= size[v] ? son[x] : v;
	}
}

void dfs2(int x, int tp){
	top[x] = tp;
	if(son[x])dfs2(son[x], tp);
	for(int i = head[x]; i; i = e[i].net){
		int v = e[i].to;
		if(v == fa[x] || v == son[x])continue;
		dfs2(v, v);
	}
}
struct que{int id, opt, ad, k;};
vector<que>v[maxn];
int ans[maxn];
int lca(int u, int v){
	while(top[u] != top[v]){
		if(dep[top[u]] < dep[top[v]])swap(u, v);
		u = fa[top[u]];
	}
	return dep[u] < dep[v] ? u : v;
}

int tmp1[maxn * 3], tmp2[maxn * 3];
int base;
void solve(int x){
	++tmp1[x + dep[x]];
	++tmp2[dep[x] - x + base];
	for(que y : v[x]){
		if(y.ad & 1){
			ans[y.id] += y.opt * tmp1[y.k];
		}else{
			ans[y.id] += y.opt * tmp2[y.k + base];
		}
	}
	for(int i = head[x]; i; i = e[i].net){
		int v = e[i].to;
		if(v == fa[x])continue;
		solve(v);
	}
	--tmp1[x + dep[x]];
	--tmp2[dep[x] - x + base];
}

int main(){
	freopen("query.in","r",stdin);
	freopen("query.out","w",stdout);
	n = read(), m = read();
	for(int i = 1; i < n; ++i){
		int u = read(), v = read();
		add(u, v); add(v, u);
	}
	base = n + 5;
	dfs1(1); dfs2(1, 1);
	for(int i = 1; i <= m; ++i){
		int l = read(), r = read();
		int lc = lca(l, r);
		int s = dep[l] + dep[r] - dep[lc] - dep[lc];
		v[l].push_back({i, 1, 1, dep[l]});
		v[r].push_back({i, 1, 2, dep[r] - s});
		v[lc].push_back({i, -1, 1, dep[l]});
		v[fa[lc]].push_back({i, -1, 2, dep[r] - s});
	}
	solve(1);
	for(int i = 1; i <= m; ++i)printf("%d\n",ans[i]);
	return 0;
}

D. 网络

考虑我们只需要保证带电的 \(>= n / 2\)

每一次的平衡,实际上是一个 \(x ,y\) 不能同时带电的条件

于是我们把满足 不能同时带电 的两根电线看成一个二元组

初始随便钦定即可, 最后一根电线单独为一个

正着向后推,考虑我们现在的二元组,其实可以理解为,其中一个带电,一个不带电,并且是任意的

所以出现一条 \(x ,y\)的平衡器时, 假设原来的配对为 \((x, a)\) \((y, b)\)

此时 \(x , y\) 不能同时带电,所以新的配对为\((x, y)\) \((a, b)\),发现此时上面的几个性质还是满足的,于是直接向后推即可

三根电线也是类似的

于是考虑如何构造方案

按照最终的分组,我们每组钦定一根电线带电进行反推

推回到一条边 \(x ,y\) 时,如果此时 \(y\) 带电,那么为 \(1\) ,否则为 \(0\)

然后考虑撤销该次配对的影响,我们此时只能改变 \(x, y\) 的电性,所以需要根据之前的\(a, b\) 此时的电性来重新确定\(x, y\)

三根线同理

我最开始认为构造时和原来一样是任意的,其实不是,因为我们此时已经有了确定的答案状态,所以没有推过来时灵活

code
#include<bits/stdc++.h>

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 = 5e6  + 5;
int n, m;
struct edge{int x, y;}e[maxn];
int oth[maxn];
bool elc[maxn];
int ans[maxn];
int opt[maxn][2];

int main(){
	freopen("network.in","r",stdin);
	freopen("network.out","w",stdout);
	n = read(), m = read();
	for(int i = 1; i <= m; ++i)e[i].x = read(), e[i].y = read();
	for(int i = 1; i <= n; i += 2)oth[i] = i + 1; if(n & 1)oth[n] = n;
	for(int i = 2; i <= n; i += 2)oth[i] = i - 1;
	for(int i = 1; i <= m; ++i){
		int x = e[i].x, y = e[i].y;
		int ox = oth[x], oy = oth[y];
		if(ox == y)continue;
		if(x == ox){
			oth[y] = x;
			oth[x] = y;
			oth[oy] = oy;
			opt[i][1] = oy;
		}else if(y == oy){
			oth[y] = x;
			oth[x] = y;
			oth[ox] = ox;
			opt[i][0] = ox;
		}else{
			oth[ox] = oy; oth[oy] = ox;
			oth[x] = y; oth[y] = x;
			opt[i][0] = ox, opt[i][1] = oy;
		}
	}
	for(int i = 1; i <= n; ++i)elc[i] = !elc[oth[i]];
	for(int i = m; i >= 1; --i){
		int x = e[i].x, y = e[i].y;
		int ox = opt[i][0], oy = opt[i][1];
		if(elc[y])ans[i] = 1;
		if(ox && oy){
			if(elc[ox])elc[x] = 0, elc[y] = 1;
			else elc[x] = 1, elc[y] = 0;
		}else if(ox){
			elc[x] = 0;
			elc[ox] = elc[y] = 1;
		}else{
			elc[y] = 0;
			elc[oy] = elc[x] = 1;
		}
	}
	printf("YES\n");
	for(int i = 1; i <= m; ++i)printf("%d",ans[i]);
	return 0;
}
posted @ 2022-10-19 16:29  Chen_jr  阅读(96)  评论(0编辑  收藏  举报