Codeforces Round #673 (Div. 2) Sep/27/2020 23:05UTC+8

Codeforces Round #673 Div. 2 Sep/27/2020 23:05UTC+8

比赛链接:https://codeforces.com/contest/1417
比赛记录:https://blog.csdn.net/cheng__yu_/article/details/105395197

C. k-Amazing Numbers

题意:给定一个长度为 n n n 的序列,问在所有 k k k 子段都出现的最小的数字是多少? k ∈ [ 1 , n ] , 1 ≤ n ≤ 3 × 1 0 5 k\in[1,n],1\le n \le 3\times 10^5 k[1,n],1n3×105

思路

  • 可以贪心地想,从最小的数字开始,它能够包含的最小区间是多大,那么后面更大的区间也是能够包含的。比如,n = 10 ,1 出现在2、3、6的位置上,那么 1 能够包含的最小区间就是 5 。因此后面的 5 - 10 的答案都是 1 。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=3e5+10;
int t,n;
int a[maxn],ans[maxn];
vector<int> vec[maxn];
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        for(int i=0; i<=n; ++i) vec[i].clear(),ans[i]=-1;
        for(int i=1; i<=n; ++i)
        {
            vec[i].push_back(0);
            vec[i].push_back(n+1);
        }
        for(int i=1; i<=n; ++i)
        {
            int x;
            scanf("%d",&x);
            vec[x].push_back(i);
        }

        int tail=n;
        for(int i=1; i<=n; ++i)
        {
            if(vec[i].size()==2) continue;
            sort(vec[i].begin(),vec[i].end());
            int dis=0;
            for(int j=1; j<vec[i].size(); ++j)
                dis=max(dis,vec[i][j]-vec[i][j-1]);
            for(int j=dis; j<=tail; ++j)
                ans[j]=i;
            tail=min(tail,dis-1);
        }
        for(int i=1; i<=n; ++i)
            printf("%d%c",ans[i],i==n?'\n':' ');
    }
    return 0;
}

D. Make Them Equal

题意:给定一个长度为 n 的数组,每次可以选择三个数 i 、 j 、 x i 、j 、x ijx ( 1 ≤ i , j ≤ n , 0 ≤ x , ≤ 1 0 9 ) (1\le i,j\le n ,0\le x,\le 10^9) 1i,jn,0x,109,使得 a i = a i − x i , a j = a j + x i a_i=a_i-xi,a_j=a_j+xi ai=aixi,aj=aj+xi。问在 3n 次操作内,能不能让所有数都相等,输出方案。在每次操作后要保持任意 a i a_i ai 始终为非负数 ( 1 ≤ a I ≤ 1 0 5 , 1 ≤ n ≤ 1 0 4 ) (1\le a_I \le 10^5,1\le n\le 10^4) (1aI105,1n104)

思路

  • 每个 a i a_i ai 的目标就是达到平均数 avg,那么就可以把 a i a_i ai 大于 avg 的部分向 a 1 a_1 a1 转移,即 a i a_i ai i i i 取模
  • 转移后我们发现这个 a i a_i ai 可能会大于 avg ,此时 − i - i i 是行不通了(会变负数),那么就可以从 a 1 a_1 a1 位置补充过来,变成 i i i 之后,然后在减 i i i ,即再向 a 1 a_1 a1 转移。(向 a 1 a_1 a1 转移的操作可以合在一起)
  • 最后可以得到,除了 a 1 a_1 a1 之外其余都为 0 0 0 。那么就从 a 1 a_1 a1 转移到其余位置就好了。
  • 总结一下:首先从 a 1 a_1 a1 转移到 a i a_i ai,让 a i a_i ai 变成 i i i 的倍数,然后 a i a_i ai a 1 a_1 a1 转移,最后从 a 1 a_1 a1 转移到 a i a_i ai,恰好在 3n 次之内完成
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+5;

int t,n;
int a[maxn];
struct Node
{
    int i,j,x;
};
vector<Node> ans;
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        ans.clear();
        scanf("%d",&n);
        ll sum=0;
        for(int i=1; i<=n; ++i) scanf("%d",&a[i]),sum+=a[i];
        if(sum%n!=0)
        {
            puts("-1");
            continue;
        }
        ll avg=sum/n;
        for(int i=2; i<=n; ++i)
        {
            if(a[i]%i!=0)
            {
                int need=i-a[i]%i;
                a[i]+=need;
                ans.push_back({1,i,need});
            }
            ans.push_back({i,1,a[i]/i});
        }
        for(int i=2; i<=n; ++i)
            ans.push_back({1,i,avg});
        printf("%d\n",ans.size());
        for(auto u: ans)
            printf("%d %d %d\n",u.i,u.j,u.x);
    }
    return 0;
}

E. XOR Inverse

题意:给定一个包含 n n n 个非负整数的数组,你可以选择一个非负整数 x x x ,对每一个 a i a_i ai 做异或操作,得到 b i b_i bi ,即, b i = a i ⊕ x b_i=a_i\oplus x bi=aix。问取怎样的 x 能够使得 b 的逆序对最少,如果有多个 x 满足条件,则取最小的 x 。输出最少的逆序对,和最小的 x 。

思路

  • 按位贪心,对每一层求逆序对,然后把当前层为 0 的放在一起,1 放在一起,继续往下贪心即可
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+5;
int n;
vector<int> a;
ll inv[32][2];
void solve(vector<int> &a,int depth)
{
    if(depth<0) return;
    if(a.size()==0) return;
    vector<int> left,right;
    ll cnt0=0,cnt1=0,sum01=0,sum10=0;
    for(int i=0; i<a.size(); ++i)
    {
        if(a[i]>>depth&1)
        {
            cnt1++;
            sum01+=cnt0;
            right.push_back(a[i]);
        }
        else
        {
            cnt0++;
            sum10+=cnt1;
            left.push_back(a[i]);
        }
    }
    inv[depth][0]+=sum01;
    inv[depth][1]+=sum10;
    solve(left,depth-1);
    solve(right,depth-1);
}
int main()
{
    scanf("%d",&n);
    a.resize(n);
    for(auto &x: a) cin>>x;
    solve(a,31);
    ll ans=0,x=0;
    for(int i=0; i<=31; ++i)
    {
        if(inv[i][0]>=inv[i][1]) ans+=inv[i][1];
        else ans+=inv[i][0],x|=(1<<i);
    }
    printf("%lld %lld\n",ans,x);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值