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;
}