CF1071题解

CF1071

Codeforces Round 517 (Div. 1, based on Technocup 2019 Elimination Round 2)

CF1071A

link

CF1071A题意

现在你有两天的时间备考NOI,两天各有 \(a\) 小时,\(b\) 小时(时空扭曲)。

每天可以看若干份笔记。编号为 \(k\) 的笔记需要看 \(k\) 小时(请不要忽略,最后输出有用)。为了考得更好,你需要最大化看的笔记份数。请你求出最多能看多少份笔记。

注意,看过的笔记需要都不相同。即使是不在同一天看的。

\((1 \leq a, b \leq 10^9)\)

CF1071A题解

这个显然从小到大的贪心选择 \(1\sim k\) 之间的笔记,然后对于第一天尽可能分配给 \(1\sim x\),如果有一个笔记看了超时,不看时间还不够,那么就把之前时间为 \((\sum_{i=1}^xi)-a\) 的那篇笔记分给第二天即可满足条件。

CF1071A代码

#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 = 1e6 + 10;
bool book[maxn];
int a, b;
vector<int> v1, v2;
signed main(){
    a = read(); b = read();
    int sum = a + b, tmp = 0,point = 1;
    while(tmp + point <= sum){tmp += point;point++;}
    tmp = 0;point--;
    for(int i = 1;i <= point;i++){
        if(tmp + i > a){book[i] = 1;book[tmp + i - a] = 0;break;}
        if(tmp + i == a){book[i] = 1;break;}
        book[i] = 1;tmp += i;
    }
    for(int i = 1;i <= point;i++){
        if(book[i])v1.push_back(i);
        else v2.push_back(i);
    }
    printf("%d\n",v1.size());for(int i : v1)printf("%d ",i);puts("");
    printf("%d\n",v2.size());for(int i : v2)printf("%d ",i);puts("");
    return 0;
}

CF1071B

link

CF1071B题意

给你一个 \(n\times n\) 的全是小写字母的矩阵,你能改变 \(k\) 个字母

你要从左上角走到右下角,且每步只能移动到右边或下边的字母上。

对于每一条路径都会得到一个由你经过的所有字母组成的字符串。当然,它的长度是 \(2\times n-1\)

在你最多改动 \(k\) 个字母的情况下,找到一个得到字符串字典序最小的路径。

CF1071B题解

\(f_{i,j}\) 表示从 \((1,1)\) 出发经过不是a字符的最少情况。
那么从 \(f_{i,j}=k\)(如果任意一个都少于 \(k\),那就从 \(f\) 最大的位置出发)出发,每次bfs搜索最优的路径即可。

CF1071B代码

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e3 + 10;
char ch[maxn][maxn];
int f[maxn][maxn];
int n, k;
char ans[maxn * 2];
bool book[maxn][maxn];
queue<pair<int,int> > que;
signed main(){
    scanf("%d%d",&n,&k);
    for(int i = 1;i <= n;i++){scanf("%s",ch[i] + 1);}
    for(int i = 0;i <= n + 1;i++)f[i][0] = f[0][i] = 0x3f3f3f3f;
    f[1][0] = 0;int st = 0, tmp = 0;
    for(int i = 1;i <= n;i++)
        for(int j = 1;j <= n;j++){
            f[i][j] = min(f[i - 1][j],f[i][j - 1]) + (ch[i][j] != 'a');
            if(f[i][j] <= k)st = max(st,i + j - 1),tmp = max(tmp,f[i][j]);
        }
    for(int i = 1;i <= n * 2;i++)ans[i] = 'z' + 1;
    if(k == 0){
        que.push(make_pair(1,1));st = 0;ans[1] = ch[1][1];
        while(!que.empty()){
            int u = que.front().first, v = que.front().second;que.pop();
            if(ch[u][v] > ans[u + v - 1])continue;
            if(v + 1 <= n){
                ans[u + v] = min(ans[u + v],ch[u][v + 1]);
                if(!book[u][v + 1] && ch[u][v + 1] == ans[u + v]){book[u][v + 1] = 1;que.push(make_pair(u,v + 1));}
            }
            if(u + 1 <= n){
                ans[u + v] = min(ans[u + v],ch[u + 1][v]);
                if(!book[u + 1][v] && ch[u + 1][v] == ans[u + v]){book[u + 1][v] = 1;que.push(make_pair(u + 1,v));}
            }
        }
    }
    else{
        for(int i = 1;i <= st && i <= n;i++)
            if(f[i][st - i + 1] == tmp){que.push(make_pair(i,st - i + 1));book[i][st - i + 1] = 1;}
        
        while(!que.empty()){
            int u = que.front().first, v = que.front().second;que.pop();
            if(ch[u][v] > ans[u + v - 1])continue;
            if(v + 1 <= n){
                ans[u + v] = min(ans[u + v],ch[u][v + 1]);
                if(!book[u][v + 1] && ch[u][v + 1] == ans[u + v]){book[u][v + 1] = 1;que.push(make_pair(u,v + 1));}
            }
            if(u + 1 <= n){
                ans[u + v] = min(ans[u + v],ch[u + 1][v]);
                if(!book[u + 1][v] && ch[u + 1][v] == ans[u + v]){book[u + 1][v] = 1;que.push(make_pair(u + 1,v));}
            }
        }
    }
    for(int i = 1;i <= st;i++){putchar('a');}
    for(int i = st + 1;i <= n * 2 - 1;i++)putchar(ans[i]);
    puts("");
    return 0;
}

CF1071C

link

CF1071C题意

给你一个长度为 \(n\) 的数组,包含\(0,1\)

你可以执行若干次操作,每次操作包含两个步骤:

  1. 选择三个位置\(x,y,z\),满足\(1\le x<y<z\le n,y-x=z-y\)
  2. 将这三个位置上的数取反(\(0\)变成\(1\)\(1\)变成\(0\))。

若存在方案使每一位都为\(0\)且操作次数不超过 \((\lfloor \frac{n}{3} \rfloor + 12)\),第一行输出\(YES\),输出方案。

若无解,输出\(NO\)

\(3\le n\le 10^5\)

CF1071C题解

暴力+打表=正解。
*2600的黑题恐怖如斯。

尝试跑暴力,有以下两个发现:

  • 所有长度大于 \(8\) 的数组一定有解。
  • 在一个长度为 \(11\) 的数组中,不超过两次就能够把 \([1,6]\) 之间的任意数字变成 \(0\)

于是先打表打出长度为 \(11\) 时将 \([1,6]\) 区间变成 \(0\) 的方法,然后剩下的直接暴力搜索即可。
需要注意的是,这道题的限制次数比较紧,尽可能的多节约次数。

CF1071C代码

#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;
const int opt[1 << 6][7] = {
{-1,-1,-1,-1,-1,-1},
{6,8,10,-1,-1,-1},
{5,8,11,-1,-1,-1},
{5,6,7,-1,-1,-1},
{4,7,10,-1,-1,-1},
{4,6,8,-1,-1,-1},
{3,4,5,3,7,11},
{4,5,6,-1,-1,-1},
{3,7,11,-1,-1,-1},
{3,6,9,-1,-1,-1},
{3,5,7,-1,-1,-1},
{1,3,5,1,6,11},
{3,4,5,5,7,9},
{2,3,4,2,6,10},
{3,4,5,-1,-1,-1},
{3,4,5,6,7,8},
{2,4,6,4,6,8},
{2,6,10,-1,-1,-1},
{2,5,8,-1,-1,-1},
{2,5,8,6,7,8},
{2,3,4,3,7,11},
{2,4,6,-1,-1,-1},
{2,3,4,3,5,7},
{2,4,6,5,7,9},
{2,3,4,4,7,10},
{1,2,3,1,6,11},
{1,2,3,1,5,9},
{2,3,4,4,5,6},
{2,3,4,-1,-1,-1},
{2,3,4,6,7,8},
{2,3,4,5,7,9},
{2,3,4,5,6,7},
{1,3,5,3,5,7},
{1,6,11,-1,-1,-1},
{1,5,9,-1,-1,-1},
{1,3,5,3,6,9},
{1,4,7,-1,-1,-1},
{1,4,7,6,7,8},
{1,4,7,5,7,9},
{1,4,7,5,6,7},
{1,3,5,5,7,9},
{1,2,3,2,6,10},
{1,3,5,-1,-1,-1},
{1,3,5,6,7,8},
{3,4,5,1,5,9},
{1,2,3,2,4,6},
{1,3,5,4,7,10},
{1,3,5,4,6,8},
{1,2,3,3,7,11},
{1,2,3,3,6,9},
{1,2,3,3,5,7},
{2,5,8,1,6,11},
{2,4,6,1,6,11},
{1,4,7,2,6,10},
{1,2,3,3,4,5},
{2,4,6,1,5,9},
{1,2,3,-1,-1,-1},
{1,2,3,6,7,8},
{1,2,3,5,7,9},
{1,2,3,5,6,7},
{1,2,3,4,7,10},
{1,2,3,4,6,8},
{2,3,4,1,5,9},
{1,2,3,4,5,6}
};
int n;
int a[maxn];
vector<pair<int,pair<int,int> > > res;

int pr[40][2], tot;
bool chose[40];
bool flag = false;
int add;
void dfs(int step,int sta,int nsta){
    if(flag)return;
    // printf("sta =  %d nsta = %d\n",sta,nsta);
    if(nsta == sta){
        int cnt = 0;
        for(int i = 0;i <= tot;i++){
            if(chose[i])res.push_back(make_pair(pr[i][0] - pr[i][1] + add,make_pair(pr[i][0] + add,pr[i][0] + pr[i][1] + add)));
        }
        flag = true;
        return;
    }
    if(step == -1){return;}
    int x1 = pr[step][0] - pr[step][1],x2 = pr[step][0],x3 = pr[step][0] + pr[step][1];
    x1 = n - x1;x2 = n - x2;x3 = n - x3;
    nsta ^= (1 << x1);nsta ^= (1 << x2);nsta ^= (1 << x3);
    chose[step] = 1; dfs(step - 1,sta,nsta);
    nsta ^= (1 << x1);nsta ^= (1 << x2);nsta ^= (1 << x3);
    chose[step] = 0;dfs(step - 1,sta,nsta);
}
signed main(){
    n = read();
    for(int i = 1;i <= n;i++)a[i] = read();
    int l = 1;
    for(int i = 1;i <= n;i += 6,l = i){
        int sta = 0;
        for(int j = 0;j < 6;j++)sta = (sta << 1) + a[i + j];
        if(opt[sta][2] + i - 1 > n || opt[sta][5] + i - 1 > n)break;
        if(opt[sta][0] != -1){
            res.push_back(make_pair(opt[sta][0] + i - 1,make_pair(opt[sta][1] + i - 1,opt[sta][2] + i - 1)));
            a[opt[sta][0] + i - 1] ^= 1;a[opt[sta][1] + i - 1] ^= 1;a[opt[sta][2] + i - 1] ^= 1;
        }
        if(opt[sta][3] != -1){
            res.push_back(make_pair(opt[sta][3] + i - 1,make_pair(opt[sta][4] + i - 1,opt[sta][5] + i - 1)));
            a[opt[sta][3] + i - 1] ^= 1;a[opt[sta][4] + i - 1] ^= 1;a[opt[sta][5] + i - 1] ^= 1;
        }
    }
    int sta = 0;
    if(n - l + 1 < 8){l = max(1,n + 1 - 8);}
    for(int j = l;j <= n;j++)sta = (sta << 1) + a[j];
    add = l - 1;
    int cnt = 0;
    for(int i = 2;i < n - l + 1;i++)
        for(int r = 1;i - r >= 1 && i + r <= n - l + 1;r++){
            pr[cnt][0] = i;pr[cnt++][1] = r;
        }
    n = n - l + 1;
    flag = 0;cnt--;tot = cnt;dfs(tot,sta,0);
    // printf("sta = %d,flag = %d\n",sta,flag);
    if(flag){
        puts("YES");
        printf("%d\n",res.size());
        for(auto i : res)printf("%d %d %d\n",i.first,i.second.first,i.second.second);
    }
    else{puts("NO");}
    return 0;
}

CF1071D

link

CF1071D题意

\(t\) 组数据,每组给定两个数 \(a,b\)
有两种操作:

  1. \(a\)\(b\) 变为它乘上任意一个质数
  2. \(a\)\(b\) 中的一个数变为它除以它的任意一个质因子

求最少的操作次数,使得 \(a,b\) 有相同的因子个数
\(a,b\le10^6,t\le10^5\)

CF1071D题解

不难想到,将每个数质因数分解之后,我们要的是 \(p_i^{k_i}\) 上面的 \(k_i\),顺序和 \(p_i\) 都不关心。
将所有得出的结果记作集合 \(S_i=\{k_1,k_2,\dots,k_x\},k_1\ge k_2\ge\dots\ge k_x\)
我们先求出所有数分解后的集合。
一共会得出不超过 \(289\) 个集合。
然后尝试计算每个集合到另一个目标状态(因子数量)的答案。
如果我们设 \(d_i\)\(i\) 的最小质因数,\(mp_{S_i,j}\) 表示从集合 \(S_i\) 到因子数量为 \(j\) 的最少步数,那么尝试从 \(mp_{S_{d_i}}\to mp_{S_i}\)
首先先说 \(S_1\),初始状态 \(mp_{S_1,1}=0\)
对于 \(mp_{S_1,i}\),可以通过在状态 \(mp_{S_1,\frac{i}{d_i}}\) 加入 \(d_i-1\) 个因数,来改变状态到 \(i\),因此有 \(mp_{S_1,i}\gets mp_{{S_1,\frac{i}{d_i}}}+d_i-1\)
为什么最少?
每次更新的时候,进行的操作无非就两个,一个是在原有的质因数上加一,一个是新建一个质因数,后者显然增加的因数个数更多,逼近答案也更快。

然后再说回 \(i\ge 2\) 的情况。
根据刚刚的结论,我们当然想从一个存在一个质因数 \(k\),在 \(i\)而在 \(j\)没有,且 \(i\) 中这个质因数最少的集合 \(S_j\) 推到 \(S_i\),因为从 \(j\to i\) 的花费就是 \(i\) 中含有质因数 \(k\) 的数量。
显然找个最小的即可。
但是这就完了吗?没有。
因为我们多了一种情况啊,就是 \(j\) 中没有的那个质因数。
怎么办呢?
这个也好办,暴力枚举因数的倍数,那么按照上面说的,有两种更新方式,一种修改这个最少的质因数,另一种直接再增加一个新的质因数满足倍数是我们想要的。
不难发现,这几种情况就已经是全部情况了,不需要官方题解中所说的Floyd
没了。

CF1071D代码

#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 = 1e6 + 10, maxm = 610;
int prime[maxn], d[maxn], tot;
bool book[maxn];
vector<int> vec[maxn];
map<vector<int>,vector<int> > mp;
signed main(){
    for(int i = 2;i < maxn;i++){
        if(!book[i]){prime[++tot] = i;d[i] = i;}
        for(int j = 1;j <= tot && prime[j] * i < maxn;j++){
            book[i * prime[j]] = 1;
            d[i * prime[j]] = prime[j];
            if(i % prime[j] == 0)break;
        }
    }
    for(int i = 2;i < maxn;i++){
        vec[i] = vec[i / d[i]];
        if(d[i] == d[i / d[i]])vec[i].back()++;
        else vec[i].push_back(2);
    }
    for(int i = 2;i < maxn;i++)sort(vec[i].begin(),vec[i].end(),greater<int>());
    
    auto &v = mp[vec[1]]; v.resize(maxm);v[1] = 0;
    for(int i = 2;i < maxm;i++)v[i] = v[i / d[i]] + d[i] - 1;
    
    for(int i = 2;i < maxn;i++){
        if(!mp.count(vec[i])){
            auto lst = vec[i];lst.pop_back();
            auto &v = mp[vec[i]], &u = mp[lst];
            v.resize(maxm);
            for(int x = 1;x < maxm;x++)v[x] = u[x] + vec[i].back() - 1;
            for(int x = 1;x < maxm;x++)
                for(int y = 2;x * y < maxm;y++)
                    v[x * y] = min(v[x * y],min(u[x] + abs(vec[i].back() - y),v[x] + y - 1));
        }
    }
    for(int i = read();i;i--){
        int u = read(), v = read();
        int ans = 0x3f3f3f3f;
        auto &x = mp[vec[u]], &y = mp[vec[v]];
        for(int j = 1;j < maxm;j++)
            ans = min(ans,x[j] + y[j]);
        printf("%d\n",ans);
    }
    return 0;
}

CF1071E

不会*3500

posted @ 2023-12-07 17:41  Call_me_Eric  阅读(48)  评论(0)    收藏  举报
Live2D