CF1774

CF1774

CF1774A

link

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

link

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 kn还好说,如果 k ∤ n k\nmid n kn 咋办
也好办,令 r e s ← n   m o d   k res\gets n\space mod \space k resn mod k
每次计算的时候如果 a i − 1 = = ⌊ n k ⌋ a_i-1==\lfloor\frac{n}{k}\rfloor ai1==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

link

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

link

CF1774D题意

给一个 n × m ( n × m ≤ 1 0 6 ) n\times m(n\times m \le 10^6) n×m(n×m106) 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 nsum1 无解,否则令 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 nsum1一定有解。
然后对于 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 cntilim 次。
而我们的解法保证了做完 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

link

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×(n1)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,有以下操作

  1. 向序列中插入一个 x x x
  2. 将序列中所有数减去 x x x,减后删去所有 ≤ 0 \le 0 0 的数
  3. 将这之前的所有操作(包括操作 3 3 3)重复一遍

求出进行完所有操作之后的序列长度

CF1774题解

首先一定要发现的一个性质是,只有操作一会引发贡献,并且操作一之间互不影响
然后考虑操作三的实质
不难发现,操作三其实就是将当前的序列 c o p y copy copy 了一份出来(因为进行了与之前相同的操作麻),并且让原来的序列进行操作二。
具体而言,设一个变量 t t t,每经过一个操作二就 t ← v a l i t\gets val_i tvali,每经过一个操作三就 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 xt 出来
突然发现,对于每个操作一,它的贡献就是将接下来的 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 cix,那么如果不选第 i i i 个数字, 1 1 1 ~ i − 1 i-1 i1 之间的数字随便选,直接 + = 2 i − 1 +=2^{i-1} +=2i1,然后让 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值