CF1198题解

CF1198

Codeforces Round 576 (Div. 1)

CF1198A

link

CF1198A题意

有一种数字化一段录音的常用方式,是记录每一个时刻的强度值。这些非负的强度值就可以代表一段音频

对于一段音频,若有 \(K\) 个不同的强度值,那么每一位我们都需要 \(k = \lceil\log_2K \rceil\) \(\text{bit}\) 来存储

也就是说,为了存储这一段音频,我们需要 \(nk\)\(\text{bit}\)

为了压缩音频大小,我们们采取如下的方式:

选择两个整数 \(l, r(l \leq r)\),对于音频的强度值序列 \(v\),将其作一次变换:

\[v_i = \begin{cases} v_i&l \leq v_i \leq r \\ l &v_i < l\\ r &r < v_i \end{cases} \]

其中第 \(2, 3\) 种情况被视作 \(v_i\) 这个强度值被更改了

你的任务是对于给定的强度值序列 \(a\),找到一组 \(l, r\),使得在经过上述压缩后能够被大小为 \(I \ \text{bytes} (\text{1bytes = 8 bit})\) 的存储器装下,并最小化被更改的强度值的数量。

CF1198A题解

坏了坏了,Eric被1600爆杀了(
首先别读错题,\([l,r]\) 表示值域范围,不是下标范围。
然后计算最多保留的不同数字 \(k = 2^{\lfloor\frac{I}{n}\rfloor}\)
直接暴力枚举左端点 \(l=i\),则 \(r=i+k-1\),预处理下在这个范围之外的数字有多少个即可。
时间复杂度 \(O(n)\)

CF1198A代码

#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 = 4e5 + 10;
int n, I;
int a[maxn], b[maxn];
int pre[maxn], suf[maxn];
int buc[maxn];
int qpow(int x,int a){
    int res = 1;
    while(a){
        if(a & 1)res = res * x;
        if(res >= n)return n;
        x = x  * x;a >>= 1;
        if(x >= n)x = n;
    }
    return res;
}
signed main(){
    n = read(); I = read() * 8;
    for(int i = 1;i <= n;i++){b[i] = a[i] = read();}
    sort(b + 1,b + 1 + n);
    int tot = unique(b + 1,b + 1 + n) - b - 1;
    for(int i = 1;i <= n;i++){a[i] = lower_bound(b + 1,b + 1 + tot,a[i]) - b;}
    for(int i = 1;i <= n;i++)buc[a[i]]++;
    for(int i = 1;i <= tot;i++)pre[i] = buc[i] + pre[i - 1];
    for(int i = tot;i;i--)suf[i] = buc[i] + suf[i + 1];
    int ans = n, k = qpow(2,I / n);
    if(k >= tot){puts("0");return 0;}
    for(int i = 1;i + k - 1 <= tot;i++) ans = min(ans,pre[i - 1] + suf[i + k]);
    printf("%lld\n",ans);

    // printf("tot = %lld, k = %lld\n",tot, k);
    return 0;
}

CF1198B

link

CF1198B题意

给出一个长度为 \(n\) 的数组 \(a\),对它进行 \(m\) 次操作,每个操作如下:

  • \(1\space u\space v\):将 \(a_u\gets v\)
  • \(2\space u\):将所有小于 \(u\) 的数改成 \(u\)

问最后的数组。

CF1198B题解

操作 \(1\) 相当于单点修改,操作 \(2\) 相当于区间取 \(max\),这东西线段树随便维护一下就OK了。

CF1198B代码

#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, a[maxn], m;
struct Segment_Tree{
    struct node{
        int maxx, tag;
        node(int maxx = 0,int tag = 0):maxx(maxx),tag(tag){}
    }d[maxn << 2];
    node mergenode(node l,node r){return node(max(l.maxx,r.maxx));}
    void pushdown(int p){
        if(d[p].tag){
            d[p << 1].maxx = max(d[p].tag,d[p << 1].maxx);
            d[p << 1].tag = max(d[p].tag,d[p << 1].tag);
            d[p << 1 | 1].maxx = max(d[p].tag,d[p << 1 | 1].maxx);
            d[p << 1 | 1].tag = max(d[p].tag,d[p << 1 | 1].tag);
            d[p].tag = 0;
        }
    }
    void build(int l,int r,int p){
        if(l == r){d[p] = node(a[l]);return;}
        int mid = l + r >> 1;
        build(l,mid,p << 1);build(mid + 1,r,p << 1 | 1);
        d[p] = mergenode(d[p << 1],d[p << 1 | 1]);
    }
    void update(int l,int r,int pos,int p,int upd){
        if(l == r && l == pos){d[p] = node(upd);return;}
        int mid = l + r >> 1;pushdown(p);
        if(pos <= mid)update(l,mid,pos,p << 1,upd);
        else update(mid + 1,r,pos,p << 1 | 1,upd);
        d[p] = mergenode(d[p << 1],d[p << 1 | 1]);
    }
    void update(int l,int r,int s,int t,int p,int upd){
        if(s <= l && r <= t){d[p].tag = max(d[p].tag,upd);d[p].maxx = max(d[p].maxx,upd);return;}
        int mid = l + r >> 1;pushdown(p);
        if(s <= mid)update(l,mid,s,t,p << 1, upd);
        if(mid < t)update(mid + 1,r,s,t,p << 1 | 1,upd);
        d[p] = mergenode(d[p << 1],d[p << 1 | 1]);
    }
    void query(int l,int r,int p){
        if(l == r){printf("%d ",d[p].maxx);return;}
        int mid = l + r >> 1;pushdown(p);
        query(l,mid,p << 1);query(mid + 1,r,p << 1 | 1);
    }
}tree;

signed main(){
    n = read();for(int i = 1;i <= n;i++)a[i] = read();
    tree.build(1,n,1);m = read();int u, opt;
    for(int i = 1;i <= m;i++){
        opt = read(); u = read();
        if(opt == 1)tree.update(1,n,u,1,read());
        else tree.update(1,n,1,n,1,u);
    }
    tree.query(1,n,1);
    return 0;
}

CF1198C

link

CF1198C题意

给一个无向图,\(3\times n\) 个点, \(m\) 条边,请找大小为 \(n\) 的点独立集或边独立集,如果都没有报告无解。

CF1198C题解

首先不存在无解情况,理由很简单,每次加入边的时候,类似最小生成树的形式选择加入哪条边。
然后如果还剩下 \(k\ge n\) 个点没有被任何加入边覆盖,那就一定有点独立集,直接输出即可。
否则表明至少有 \(k\ge 2\times n+1\) 个点是联通的,这些点至少组成 \(n\) 条边,直接输出即可。
故不存在无解的情况,解法也在上文说完了。

CF1198C代码

#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;
int book[maxn * 3], ans[maxn];
signed main(){
int T = read();
while(T--){
    n = read() * 3;m = read();int cnt = 0;
    for(int i = 1;i <= n;i++)book[i] = 0;
    for(int i = 1;i <= m;i++){
        int u = read(), v = read();
        if(!book[u] && !book[v] && cnt < n / 3){book[u] = book[v] = 1;ans[++cnt] = i;}
    }
    if(cnt >= n / 3){
        puts("Matching");
        for(int i = 1;i <= n / 3;i++){printf("%d ",ans[i]);}
        puts("");
    }
    else{
        puts("IndSet");cnt = 0;
        for(int i = 1;i <= n && cnt < n / 3;i++){
            if(!book[i]){cnt++;printf("%d ",i);}
        }
        puts("");
    }
}
    return 0;
}

CF1198D

link

CF1198D题意

\(n\times n\) 矩阵只含 .#,请用价值和最少的一堆长方形覆盖所有的 #
一个长方形的价值是长与宽的最大值。

CF1198D题解

\(f_{xl,yl,xr,yr}\) 表示将 \((xl,yl)\to (xr,yr)\) 的矩形之间的所有 # 覆盖的最小答案。
然后对每个 \(f_{xl,yl,xr,yr}\),枚举竖向分割线和横向分割线,转移显然。
然后记搜解决即可。

CF1198D代码

#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 = 55;
int f[maxn][maxn][maxn][maxn];
char ch[maxn][maxn];
int n;
int dfs(int xl,int yl,int xr,int yr){
    if(f[xl][yl][xr][yr] != -1)return f[xl][yl][xr][yr];
    int ans = max(xr - xl + 1,yr - yl + 1);
    for(int i = xl;i < xr;i++)ans = min(ans,dfs(xl,yl,i,yr) + dfs(i + 1,yl,xr,yr));
    for(int i = yl;i < yr;i++)ans = min(ans,dfs(xl,yl,xr,i) + dfs(xl,i + 1,xr,yr));
    return f[xl][yl][xr][yr] = ans;
}
signed main(){
    n = read();
    memset(f,-1,sizeof(f));
    for(int i = 1;i <= n;i++){
        scanf("%s",ch[i] + 1);
        for(int j = 1;j <= n;j++)
            f[i][j][i][j] = ch[i][j] == '#';
    }
    dfs(1,1,n,n);
    printf("%d\n",f[1][1][n][n]);
    return 0;
}

CF1198E

link

CF1198E题意

有一个 \(n*n\) 的网格,网格中有一些点是黑的,其他点都是白的。你每次可以花费 \(\min(h,w)\) 的代价把一个 \(h*w\) 的矩形区域变白。求把所有黑格变白的最小代价。

CF1198E题解

首先发现一个结论,就是每次操作一定是选定一个 \(1\times n\) 或者 \(n\times 1\) 的矩形,这个一定是最优的,证明显然。
将行的集合记作 \(A\),列的集合记作 \(B\),然后对于每个黑点,将相应的行列连边,然后跑最小覆盖即可。
至于这道题的巨大 \(n\) 值,直接离散化即可。

CF1198E代码

#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 = 310,INF = 0x3f3f3f3f;
int xl[maxn], xr[maxn], yl[maxn], yr[maxn];
int x[maxn], y[maxn];
int n, m;

struct edge{
    int to, nexte, cap, flow;
    edge(int to = 0,int ne = 0,int cap = 0,int flow = 0):to(to),nexte(ne),cap(cap),flow(flow){}
}e[maxn * maxn * 3];
int head[maxn * 10], tot = 1;
void add(int u,int v,int cap){e[++tot] = edge(v,head[u],cap);head[u] = tot;}
void addd(int u,int v,int cap){add(u, v, cap);add(v, u, 0);}

int cur[maxn * 10], dis[maxn * 10];bool book[maxn * 10];
queue<int> que;
bool bfs(int S,int T){
    while(!que.empty())que.pop();
    for(int i = 1;i <= T;i++)dis[i] = INF, cur[i] = book[i] = 0;
    que.push(S);dis[S] = 0;cur[S] = head[S];book[S] = 1;
    while(!que.empty()){
        int u = que.front(); que.pop();
        for(int i = head[u];i;i = e[i].nexte){
            int v = e[i].to;
            if(dis[v] == INF && e[i].cap > e[i].flow){
                dis[v] = dis[u] + 1;cur[v] = head[v];
                if(!book[v]){que.push(v);book[v] = 1;}
            }
        }
    }
    return dis[T] != INF;
}
int dfs(int u,int flow,int T){
    if(u == T || !flow)return flow; int res = 0;
    for(int i = cur[u];i;i = e[i].nexte){
        int v = e[i].to;cur[u] = i;
        if(e[i].cap > e[i].flow && dis[v] == dis[u] + 1){
            int tmp = dfs(v, min(e[i].cap - e[i].flow,flow),T);
            if(!tmp){dis[v] = INF;continue;}
            e[i].flow += tmp;e[i ^ 1].flow -= tmp;
            res += tmp;flow -= tmp;
            if(!flow)return res;
        }
    }
    return res;
}
int Dinic(int S,int T){
    int mxflow = 0;
    while(bfs(S,T))mxflow += dfs(S,INF,T);
    return mxflow;
}

signed main(){
    n = read(); m = read();
    for(int i = 1;i <= m;i++){
        xl[i] = read();yl[i] = read(); xr[i] = read() + 1;yr[i] = read() + 1;
        x[i * 2 - 1] = xl[i];x[i * 2] = xr[i];
        y[i * 2 - 1] = yl[i];y[i * 2] = yr[i];
    }
    x[m * 2 + 1] = 1;x[m * 2 + 2] = n + 1;
    y[m * 2 + 1] = 1;y[m * 2 + 2] = n + 1;
    sort(x + 1,x + m * 2 + 2 + 1);int totx = unique(x + 1,x + m * 2 + 2 + 1) - x - 1;
    sort(y + 1,y + m * 2 + 2 + 1);int toty = unique(y + 1,y + m * 2 + 2 + 1) - y - 1;
    // for(int i = 1;i <= totx;i++)printf("x[%d]=%d\n",i,x[i]);
    for(int i = 1;i <= m;i++){
        xl[i] = lower_bound(x + 1,x + 1 + totx, xl[i]) - x;
        xr[i] = lower_bound(x + 1,x + 1 + totx, xr[i]) - x;
        yl[i] = lower_bound(y + 1,y + 1 + toty, yl[i]) - y;
        yr[i] = lower_bound(y + 1,y + 1 + toty, yr[i]) - y;
        for(int l = xl[i];l < xr[i];l++)
            for(int r = yl[i];r < yr[i];r++)
                addd(l,r + totx,INF);
    }
    int S = totx + toty + 1, T = totx + toty + 2;
    for(int i = 1;i < totx;i++) addd(S,i,x[i + 1] - x[i]);
    for(int i = 1;i < toty;i++) addd(i + totx,T,y[i + 1] - y[i]);
    printf("%d\n",Dinic(S,T));
    return 0;
}

CF1198F

link

CF1198F题意

给你一个长度为 \(n\) 的数组 \(a\),让你把它划分成两组,使得这两组所有数的 \(\gcd=1\),给出方案或者报告无解。

CF1198F题解

神秘随机化,使我的大脑旋转(
先说一个假的贪心:每次尝试向第一组填入一个数字,如果能够使得 \(\gcd\) 变小就加入,否则加入第二组。
这个显然是假的,但是如果你对他使用random_shuffle并多跑几次,那真是泰库拉就能获得乱搞正解通过的好分数啦!
为啥?
想一个问题,就是对一个集合尽可能的加入数字,一定不会使得 \(\gcd\) 变大对叭?
然后你进行random_shuffle,会尽可能减少第一组的数字,也就是使得第二组的数字尽可能多,就很有可能使得出现答案。

CF1198F代码

#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;
bool book[maxn];
struct node{
    int val, id;
    node(int val = 0,int id = 0):val(val),id(id){}
}a[maxn];
signed main(){
    n = read();
    for(int i = 1;i <= n;i++){a[i] = node(read(),i);}
for(long long st = clock();clock() - st < CLOCKS_PER_SEC * 0.45;){
    random_shuffle(a + 1,a + 1 + n);
    memset(book,0,sizeof(book));
    int g1 = a[1].val, g2 = 0;book[a[1].id] = 1;
    for(int i = 2;i <= n;i++){
        if(__gcd(g1,a[i].val) < g1){
            book[a[i].id] = 1;
            g1 = __gcd(g1,a[i].val);
        }
        else g2 = __gcd(g2,a[i].val);
    }
    if(g1 == 1 && g2 == 1){
        puts("YES");
        for(int i = 1;i <= n;i++)
            putchar('1' + book[i]),putchar(' ');
        puts("");return 0;
    }
}
    puts("NO");
    return 0;
}
posted @ 2023-12-01 08:15  Call_me_Eric  阅读(36)  评论(0)    收藏  举报
Live2D