Codeforces Round #609 (Div. 2)

场次链接
A. Equation
题目链接
给你一个数,让你给出两个合数a,b,使得a-b等于该数。
数据范围 1 ≤ n ≤ 1 0 7 1\leq n\leq 10^7 1n107, 1 ≤ a , b ≤ 1 0 9 1\leq a,b\leq 10^9 1a,b109
n ∗ 9 − n ∗ 8 n*9-n*8 n9n8即可,或者 n ∗ 3 − n ∗ 2 n*3-n*2 n3n2然后特判1。
复杂度 O ( n ) O(n) O(n)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
void work()
{
    ll n;
    scanf("%lld",&n);
    if(n==1){
        printf("9 8\n");
        return;
    }else{
        printf("%lld %lld\n",n*3,n*2);
    }
}
int main()
{
    ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    int T;
    //scanf("%d",&T);
    //cin>>T;
    T=1;
    while(T--){
        work();
    }
}

B. Modulo Equality
题目链接
首先一个n ,m,然后给n长度的a数组和b数组,b数组为a数组每个数加上同一个数之后%m,顺序不一定相同,问 加上的这个数为多少。
数据范围 1 ≤ n ≤ 2000 1\leq n\leq2000 1n2000, 1 ≤ m ≤ 1 0 9 1\leq m\leq 10^9 1m109, 0 ≤ a i , b i < m 0\leq a_i,b_i<m 0ai,bi<m
解 a[1]一定对应一个b,所以枚举b,即枚举加上的这个数,然后开一个新的数组c存a+这个数,把b和c进行sort然后判断是否完全相同,然后求最小值就可以了。
复杂度 O ( n 2 ) O(n^2) O(n2)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int a[2005];
int b[2005];
int c[2005];
void work()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    for(int i=1;i<=n;i++){
        scanf("%d",&b[i]);
    }
    sort(b+1,b+n+1);
    int ans=m;
    for(int i=1;i<=n;i++){
        int tmp=(b[i]-a[1]+m)%m;
        for(int j=1;j<=n;j++){
            c[j]=(a[j]+tmp)%m;
        }
        sort(c+1,c+n+1);
        for(int j=1;j<=n;j++){
            if(c[j]!=b[j]){
                break;
            }
            if(j==n){
                ans=min(ans,tmp);
            }
        }
    }
    printf("%d\n",ans);
}
int main()
{
    ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    int T;
    //scanf("%d",&T);
    //cin>>T;
    T=1;
    while(T--){
        work();
    }
}

C. Long Beautiful Integer
题目链接
给出n,k,然后给出为n长度的一个整数,然后你要给出一个数,这个数的要求为第i位与第i+k位相同,且要比给出的整数大的数中的最小的那个,输出数的长度和这个数的值。
数据范围 2 ≤ n ≤ 200000 2\leq n\leq 200000 2n200000, 1 ≤ k < n 1\leq k< n 1k<n, 0 ≤ a i ≤ 9 0\leq a_i\leq 9 0ai9
解 首先直接把给的数的前k位按顺序复制到后面,如果已经比后面大了,那么直接输出。否则 在第k位加一,那么前k-1位与原数相同,第k位比原数大,必定是满足题目要求的数。注意加1后超过9的情况。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
char a[200005];
char b[200005];
void work()
{
    int n,m;
    scanf("%d%d",&n,&m);
    scanf("%s",a);
    for(int i=0;i<n;i++){
        b[i]=a[i%m];
    }
    if(strcmp(b,a)>=0){
        printf("%d\n",n);
        for(int i=0;i<n;i++){
            printf("%c",b[i]);
        }
        printf("\n");
        return;
    }
    int pos=m-1;
    while(b[pos]=='9'){
        b[pos]='0';
        pos--;
    }
    b[pos]++;
    printf("%d\n",n);
    for(int i=0;i<n;i++){
        printf("%c",b[i%m]);
    }
    printf("\n");
 
}
int main()
{
    ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    int T;
    //scanf("%d",&T);
    //cin>>T;
    T=1;
    while(T--){
        work();
    }
}

D. Domino for Young
题目链接
给你一个n长度的数组,例如给出a={3,2,2,2,1}时为下图。
在这里插入图片描述
你可以将一个12或者21的方块放到图中,问最多能放下几个方块。
数据范围 1 ≤ n ≤ 300000 1\leq n\leq 300000 1n300000 , 1 ≤ a i ≤ 300000 1\leq a_i\leq 300000 1ai300000, a i + 1 ≤ a i a_{i+1}\leq a_i ai+1ai
解 首先如果前面没有格子剩余,那么如果出现一个偶数长度的点,可以直接放完;如果前面格子有剩余,出现一个偶数值的点,不会对格子的剩余有影响,但是会改变剩余格子的位置(画一画就知道了),每两个一循环。
如果前面没有格子剩余,出现一个奇数值的点,那么会在最下面多一个格子;
如果前面格子有剩余,出现一个奇数值的点,如果剩余的是最下面的格子,那么可以把这个格子用掉,如果剩余的是第二个格子,那么如果要补上那个格子,这列会空出2个格子,故删掉前面那个格子更优。然后画图可得若要出现第一种情况,2个奇数值的位置的奇偶性要不同。
所以对于值为奇数的位置,如果位置奇偶性不同,那么可以互补,全部填满,否则把多的删掉更优。
所以在该位置值为奇数时,记录一下位置的奇偶,然后答案就是 m i n ( 奇 , 偶 ) + ∑ i = 1 n a [ i ] / 2 min(奇,偶)+\sum_{i=1}^{n}a[i]/2 min()+i=1na[i]/2.
复杂度 O ( n ) O(n) O(n)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int a[300005];
void work()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    ll ans=0;
    int tmp1=0;
    int tmp2=0;
    for(int i=1;i<=n;i++){
        ans+=a[i]/2;
        if(a[i]%2==1){
            if(i%2==0){
                tmp1++;
            }else{
                tmp2++;
            }
        }
    }
    printf("%lld\n",ans+min(tmp1,tmp2));
}
int main()
{
    ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    int T;
    //scanf("%d",&T);
    //cin>>T;
    T=1;
    while(T--){
        work();
    }
}

E. K Integers
题目链接
给你一个n长度的从1到n的乱序排列,你可以交换相邻2个位置的值,然后要出现 1 , 2 , 3 , 4 , … … , k 1,2,3,4,……,k 1,2,3,4,,k 这样的有序子段,对于k从1到n每个都输出最小的交换次数。
数据范围 1 ≤ n ≤ 200000 1\leq n\leq 200000 1n200000
解 首先 如果从i到i+k-1已经是1到k的排列,但是是乱序的,那么交换次数即这个序列中的逆序对的数量。那么就是将1到k聚集起来所需要的最少次数加上逆序对数量即可。要将1到k聚集起来,肯定是选最中间的点作为聚集点,两边都往中间靠。
先考虑计算逆序对数量,将值从1到n的位置加入一个树状数组,然后查找比它位置靠前的有多少个,即正序的数量,减一下即逆序数量。
再是将1到k聚集起来,用一个权值线段树,记录位置,从1到n循环时把当前的位置加入线段树,然后找到中点,设中点为p,那么在中点右边的要移到 p + 1 , p + 2. … … p+1,p+2.…… p+1,p+2.,在中点左边的要移到 p − 1 , p − 2 , … … p-1,p-2,…… p1,p2,,用权值线段树求出前 ( i + 1 ) / 2 − 1 (i+1)/2-1 (i+1)/21个位置的和,即中点右边的和,减去 p + 1 , p + 2 , … … p + ( i + 1 ) / 2 − 2 p+1,p+2,……p+(i+1)/2-2 p+1,p+2,p+(i+1)/22的和,即右边要移动多少。同理求出左边的。
复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int a[200005];
int tr[200005<<2];
ll sum[200005<<2];
int pos[200005];
int bit[200005];
ll pre[200005];
void add(int x)
{
	for(int i=x;i<=200000;i+=i&(-i)){
		bit[i]++;
	}
}
int query(int x)
{
	int sum=0;
	for(int i=x;i>0;i-=i&(-i)){
		sum+=bit[i];
	}
	return sum;
}
void update(int l,int r,int rt,int t)
{
	if(l==r&&r==t){
		tr[rt]++;
		sum[rt]=l;
		return;
	}
	int m=(l+r)>>1;
	if(t<=m)update(l,m,rt<<1,t);
	else update(m+1,r,rt<<1|1,t);
	tr[rt]=tr[rt<<1]+tr[rt<<1|1];
	sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
int query1(int l,int r,int rt,int t)
{
	//printf("%d %d\n",l,r);
	if(l==r){
		return l;
	}
	int m=(l+r)>>1;
	if(t>tr[rt<<1|1])return query1(l,m,rt<<1,t-tr[rt<<1|1]);
	else return query1(m+1,r,rt<<1|1,t);
}
ll query2(int l,int r,int rt,int t)
{
	if(t==0)return 0;
	if(l==r){
		return sum[rt];
	}
	int m=(l+r)>>1;
	ll ans=0;
	//printf("%d %d %d %d\n",l,r,t,tr[m]);
	if(t>tr[rt<<1|1])ans+=query2(l,m,rt<<1,t-tr[rt<<1|1])+sum[rt<<1|1];
	else ans+=query2(m+1,r,rt<<1|1,t);
	//printf("%d %d %lld\n",l,r,ans);
	return ans;
}
void work()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
    	scanf("%d",&a[i]);
    	pos[a[i]]=i;
    }
    for(int i=1;i<=n;i++){
    	pre[i]=i-query(pos[i])-1;
    	add(pos[i]);
    }
    for(int i=1;i<=n;i++){
    	pre[i]+=pre[i-1];
    }
    printf("0");
    update(1,n,1,pos[1]);
    ll sum=pos[1];
    for(int i=2;i<=n;i++){
    	ll tmp=pre[i];
    	update(1,n,1,pos[i]);
    	sum+=pos[i];
    	int mid=(i+1)/2;
    	int p=query1(1,n,1,mid);
    	//printf("p=%d\n",p);
    	//printf("%lld\n",tmp);
    	tmp+=query2(1,n,1,mid)-1ll*(p+p+mid-1)*(mid)/2;
    	//printf("???%d %lld %lld\n",mid,query2(1,n,1,mid),1ll*(p+p+mid-1)*(mid)/2);
    	//printf("%lld\n",tmp);
    	//printf("???%d %d %lld\n",mid,(p-1+p-i+mid)*(i-mid)/2,sum-query2(1,n,1,mid));
    	tmp+=1ll*(p-1+p-i+mid)*(i-mid)/2-(sum-query2(1,n,1,mid));
    	printf(" %lld",tmp);
    }
    printf("\n");
}
int main()
{
    ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    int T;
    //scanf("%d",&T);
    //cin>>T;
    T=1;
    while(T--){
        work();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值