CF1774
CF1774A
CF1774A题意
给出一个 01 01 01 串,让你在其中填入 + + + 和 − - −, 使得最后答案的绝对值尽可能小
CF1774A题解
- 遇到 0 0 0 填什么都行
- 遇到 1 1 1 交替填正负
CF1774A代码
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int x = 0, f = 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
return x * f;
}
const int maxn = 110;
int n;char ch[maxn];
signed main(){
int T = read();
while(T--){
n = read();scanf("%s",ch + 1);
bool f = (ch[1] == '1');
for(int i = 2;i <= n;i++){
if(ch[i] == '1'){putchar(f ? '-' : '+');f = !f;}
else putchar('+');
}
puts("");
}
return 0;
}
CF1774B
CF1774B题意
一张纸条有 n n n 个点,有 m m m 种颜料,每种能染 a i a_i ai 个点,请问能否构造出一种染色方案,使得任意连续 k k k 个格子都没有相同的颜色
CF1774B题解
不难想到抽屉原理
只需要判断是否
∃
i
,
a
i
<
⌊
n
k
⌋
\exist i,a_i< \lfloor\frac{n}{k}\rfloor
∃i,ai<⌊kn⌋ 就好了~~~~~~~~~吗?
然后就会Wrong answer on test 2
突然发现如果
k
∣
n
k\mid n
k∣n还好说,如果
k
∤
n
k\nmid n
k∤n 咋办
也好办,令
r
e
s
←
n
m
o
d
k
res\gets n\space mod \space k
res←n mod k
每次计算的时候如果
a
i
−
1
=
=
⌊
n
k
⌋
a_i-1==\lfloor\frac{n}{k}\rfloor
ai−1==⌊kn⌋ 令
r
e
s
−
−
res--
res−−
最后判一下
r
e
s
>
0
res>0
res>0 就好了
CF1774B代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
int x = 0, f = 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
return x * f;
}
const int maxn = 1e5 + 10;
int a, n, m, k;
signed main(){
int T = read();
while(T--){
n = read();m = read(); k = read();
bool flag = true;
int res = n % k;
for(int i = 1;i <= m;i++){
a = read();
if(a - 1 == n / k){res--;}
else if(a > n / k){flag = false;}
// printf("flag = %d;",flag);
}
if(!flag || res < 0){
// printf("%d %d,",flag,res);
puts("NO");
}
else puts("YES");
}
return 0;
}
CF1774C
CF1774C题意
刚开始题意读错了怎么办
有
n
n
n 个人,有一个环境变量
s
i
s_i
si,如果是
0
0
0 则编号小的人生存,反之编号大者生存
现在有一个
01
01
01 串,对于第
i
i
i 位,令前
i
+
1
i+1
i+1 个人两两进行决斗,第
k
k
k 次决斗环境变量是
s
k
s_k
sk,但是决斗顺序由你决定。
请问对于第
i
i
i 位,可能生存下来的人有多少个。
CF1774C题解
刚开始读错在决斗环境变量必须按顺序
可以发现最终答案取决于临近自己的连续
0
0
0 或
1
1
1 串长度
然后这玩应是可以
O
(
1
)
O(1)
O(1) 计算的
CF1774C代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 10;
int n;char ch[maxn];
signed main(){
int T;scanf("%d",&T);
while(T--){
scanf("%d",&n);scanf("%s",ch + 1);
int mx0 = 0, mx1 = 0,len0 = 0, len1 = 0;
for(int i = 1;i < n;i++){
len0 = (ch[i] == '0') ? (len0 + 1) : 0;
len1 = (ch[i] == '1') ? (len1 + 1) : 0;
printf("%d ",i - max(len0,len1) + 1);
}
puts("");
}
return 0;
}
CF1774D
CF1774D题意
给一个
n
×
m
(
n
×
m
≤
1
0
6
)
n\times m(n\times m \le 10^6)
n×m(n×m≤106) 的
01
01
01 矩阵。
可以进行一次操作:交换
a
i
,
k
,
a
j
,
k
a_{i,k},a_{j,k}
ai,k,aj,k。
要求尽可能少的操作,使每一行的
1
1
1 的数量相同。
给出方案。
CF1774D题解
不会证正确性于是自闭114514秒
给出一个解法:
- 设 s u m 1 sum_1 sum1 表示整个矩阵的 1 1 1 的数量, c n t i cnt_i cnti 表示每一行的 1 1 1的数量。
- 首先如果 n ∤ s u m 1 n\nmid sum_1 n∤sum1 无解,否则令 l i m = s u m 1 n lim=\frac{sum_1}{n} lim=nsum1。
- 每次找出一个 c n t i > l i m cnt_i>lim cnti>lim。
- 为 i i i 找到 c n t j < l i m cnt_j<lim cntj<lim,并尝试用 i i i 填 j j j 直到 c n t j = l i m cnt_j=lim cntj=lim。
- 依次进行直到没有 c n t i > l i m cnt_i>lim cnti>lim。
正确性交给读者证明
还是我说吧
首先如果
n
∣
s
u
m
1
n\mid sum_1
n∣sum1一定有解。
然后对于
c
n
t
i
>
l
i
m
cnt_i>lim
cnti>lim,一定是要把它的一些
1
1
1 点和其他的
0
0
0 点交换位置。
而这样的交换对于每一个
c
n
t
i
>
l
i
m
cnt_i>lim
cnti>lim 一定要进行
c
n
t
i
−
l
i
m
cnt_i-lim
cnti−lim 次。
而我们的解法保证了做完
c
n
t
i
>
l
i
m
cnt_i>lim
cnti>lim 之后不会出现新的
c
n
t
j
>
l
i
m
cnt_j>lim
cntj>lim。
故一定是最少的。
CF1774D代码
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int x = 0, f = 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
return x * f;
}
const int maxn = 1e5 + 10;
int n, m;
vector<int> vec[maxn];
int cnt[maxn];
vector<int> need, rest;
vector<pair<pair<int,int>,int> > opt;
signed main(){
int T = read();
while(T--){
n = read(); m = read();
int sum = 0;
for(int i = 1;i <= n;i++){
vec[i].clear();cnt[i] = 0;
for(int j = 0;j < m;j++){
vec[i].push_back(read());
cnt[i] += vec[i][j];
}
sum += cnt[i];
}
if(sum % n){puts("-1");continue;}
opt.clear();int res = sum / n;rest.clear();need.clear();
// printf("res=%d,,sum=%d\n",res,sum);
for(int i = 1;i <= n;i++){
// printf("cnt[%d]=%d\n",i,cnt[i]);
if(cnt[i] > res){rest.push_back(i);}
if(cnt[i] < res){need.push_back(i);}
}
while(!rest.empty()){
int i = rest.back();rest.pop_back();
while(!need.empty()){
int j = need.back();need.pop_back();
for(int k = 0;k < m && cnt[i] > res;k++){
// printf("i=%d,j=%d,k=%d,ik=%d,jk=%d\n",i,j,k,vec[i][k],vec[j][k]);
if((vec[i][k] == 1) && (vec[j][k] == 0)){
opt.push_back(make_pair(make_pair(i,j),k));
cnt[i]--;cnt[j]++;vec[i][k] = 0;vec[j][k] = 1;
if(cnt[i] == res || cnt[j] == res)break;
}
}
if(cnt[j] < res){need.push_back(j);break;}
}
}
printf("%d\n",opt.size());
for(auto i : opt){
printf("%d %d %d\n",i.first.first,i.first.second,i.second + 1);
}
}
return 0;
}
CF1774E
CF1774E题意
给你一棵树,每条边长度是 1 1 1,给你一对拴着长度为 d d d 的绳子的棋子,每个棋子都有自己要走到的节点,初始都在根节点,请问这两个棋子走遍自己的节点所用的距离之和是多少
CF1774E题解
考虑每个节点需不需要被这两个棋子走,设为
n
e
e
d
u
,
0
/
1
need_{u,0/1}
needu,0/1
需要走当且仅当
- 自己所在子树中有这个棋子需要走的节点
- 自己所在子树中有另一个棋子需要走的节点,并且最深的节点到自己距离超过了绳长
然后让
a
n
s
=
4
×
(
n
−
1
)
−
2
×
∑
i
=
2
n
(
n
e
e
d
u
,
0
+
n
e
e
d
u
,
1
)
ans=4\times(n-1)-2\times\sum_{i=2}^n(need_{u,0}+need_{u,1})
ans=4×(n−1)−2×∑i=2n(needu,0+needu,1)
然后就没了
CF1774E代码
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int x = 0, f= 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
return x * f;
}
const int maxn = 2e5 + 10;
int n, d;
vector<int> edg[maxn];
int dep[maxn],mdep[maxn][2];
bool book[maxn][2];
bool need[maxn][2];
void dfs(int u,int f,int opt){
dep[u] = dep[f] + 1;
if(need[u][opt])book[u][opt] = 1;
if(need[u][!opt])mdep[u][!opt] = dep[u];
for(int v : edg[u]){
if(v != f){
dfs(v, u,opt);
mdep[u][!opt] = max(mdep[u][!opt],mdep[v][!opt]);
book[u][opt] |= book[v][opt];
}
}
if(mdep[u][!opt] - dep[u] >= d)book[u][opt] |= 1;
}
signed main(){
n = read(); d = read();int u, v;
for(int i = 1;i < n;i++){
u = read(); v = read();
edg[u].push_back(v); edg[v].push_back(u);
}
for(int i = read();i;i--)need[read()][0] = 1;
for(int i = read();i;i--)need[read()][1] = 1;
dfs(1,0,0); dfs(1,0,1);
int ans = 4 * (n - 1);
for(int i = 2;i <= n;i++){ans -= (2 * (!book[i][0]) + 2 * (!book[i][1]));}
printf("%d\n",ans);
return 0;
}
CF1774F1&F2
CF1774F题意
现在维护一个序列 S S S,有以下操作
- 向序列中插入一个 x x x
- 将序列中所有数减去 x x x,减后删去所有 ≤ 0 \le 0 ≤0 的数
- 将这之前的所有操作(包括操作 3 3 3)重复一遍
求出进行完所有操作之后的序列长度
CF1774题解
首先一定要发现的一个性质是,只有操作一会引发贡献,并且操作一之间互不影响
然后考虑操作三的实质
不难发现,操作三其实就是将当前的序列
c
o
p
y
copy
copy 了一份出来(因为进行了与之前相同的操作麻),并且让原来的序列进行操作二。
具体而言,设一个变量
t
t
t,每经过一个操作二就
t
←
v
a
l
i
t\gets val_i
t←vali,每经过一个操作三就
v
a
l
i
=
t
,
t
=
t
×
2
val_i=t,t=t\times2
vali=t,t=t×2
然后每次操作三就是
c
o
p
y
copy
copy 一份
x
−
t
x-t
x−t 出来
突然发现,对于每个操作一,它的贡献就是将接下来的
c
o
p
y
copy
copy 减去的数字构成一个集合,问这个集合有多少个子集满足所有数字的和小于
x
x
x,集合大小
log
2
n
\log_2n
log2n
如果硬做的话是
O
(
l
o
g
2
n
)
O(log^2n)
O(log2n) 的,简单版本能过,但是困难版本不好说了
注意到,一段连续的操作二可以优化成一个,于是乎对于一个操作一,它之后的操作就是形如
13232333232333
13232333232333
13232333232333的
然后发现每经过一个操作三,减去的数字至少翻倍
于是发现,设第
i
i
i 次减去的数字是
c
i
c_i
ci
- 如果 c i > x c_i>x ci>x,那么第 i i i 个数字一定不能选
- 如果 c i ≤ x c_i\le x ci≤x,那么如果不选第 i i i 个数字, 1 1 1 ~ i − 1 i-1 i−1 之间的数字随便选,直接 + = 2 i − 1 +=2^{i-1} +=2i−1,然后让 x − = c i x-=c_i x−=ci 以计算选第 i i i 个数字的情况
然后就没了
注意可能在开始的一个操作一后面跟了好多操作三,这样的操作就是翻倍,没有减少的意义,特判一下
CF1174F代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
int x = 0, f= 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
return x * f;
}
const int maxn = 8e5 + 10, mod = 998244353,maxx = 1e9;
int n;
int a[maxn], b[maxn];
vector<int> vec;
int pw[maxn];
signed main(){
n = read();int sum = 0;pw[0] = 1;
for(int i = 1;i <= n;i++){
pw[i] = pw[i - 1] << 1;pw[i] %= mod;
a[i] = read();if(a[i] != 3)b[i] = read();
if(a[i] == 2){sum += b[i];}sum = min(sum,maxx);
if(a[i] == 3){b[i] = sum;sum <<= 1;}sum = min(sum,maxx);
}
int ans = 0, times = 1;sum = 0;
for(int i = n;i;i--){
switch(a[i]){
case 1:{
int x = b[i] - sum, res = 1;if(x <= 0)break;
for(int i = 0;i < vec.size();i++){
if(vec[i] < x){
res = (res + pw[vec.size() - i - 1]) % mod;
x -= vec[i];
}
}
ans = (ans + res * times) % mod;
break;
}
case 2:{sum += b[i];break;}
case 3:{
if(b[i] >= maxx)break;
if(b[i] == 0){times <<= 1;times %= mod;break;}
vec.push_back(b[i]);
break;
}
default: break;
}
}
printf("%lld\n",ans);
return 0;
}