Codeforces Round #672(Div.2) A,B,C1,C2,D

A. Cubes Sorting

判断逆序数个数是否大于n*(n-1)/2,需要时间复杂度O(nlogn)。归并排序或者树状数组都可,这里是归并排序。

#include <iostream>
#include <stdio.h>
using namespace std;
const int maxn=1e5+5;
int a[maxn];//原数组
int b[maxn];//辅助数组
long long ans;
void merge(int l,int r,int *c)//合并
{
    int mid=(l+r)>>1;
    int i=l,j=mid+1,cnt=l;
    while(i<=mid&&j<=r)
    {
        //小的放
        if(c[i]>c[j])
        {
            b[cnt++]=c[j++];
            ans+=mid-i+1;
        }
        else
        b[cnt++]=c[i++];
    }
    //把没比较的直接放
    while(i<=mid)
    b[cnt++]=c[i++];
    while(j<=r)
    b[cnt++]=c[j++];
    for(int k=l;k<=r;k++)//更新a数组
    c[k]=b[k];
}
void mergesort(int l,int r,int *c)
{
    if(l==r)
    return;
    int mid=(l+r)>>1;
    mergesort(l,mid,c);
    mergesort(mid+1,r,c);
    merge(l,r,c);
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
    ans=0;
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    scanf("%d",&a[i]);
    mergesort(1,n,a);
    if(1ll*n*(n-1)/2-1<ans)
    puts("NO");
    else
    puts("YES");
    }
    return 0;
}

B. Rock and Lever

我们发现,如果两个数最高位是在同一位,那么这两个就能贡献一组,所以,排序一下,线性扫一下更新答案即可。

#include <iostream>
#include <stdio.h>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N=1e5+5;
int T,n;
int a[N];
int get(int x)
{
    int cnt=0;
    while(x)
    {
        x>>=1;
        cnt++;
    }
    return cnt;
}
int main()
{
    scanf("%d",&T);
    //cout<<get(8)<<endl;
    while(T--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;++i)
        scanf("%d",&a[i]);
        sort(a+1,a+1+n);
        int l=1,r=1;
        long long ans=0;
        while(r<=n)
        {
            if(get(a[l])<get(a[r]))
            {
                ll tmp=1ll*(r-l);
                ans+=tmp*(tmp-1)/2;
                l=r;
            }
            else
            {
                r++;
            }
        }
        ll tmp=1ll*(r-l);
        ans+=tmp*(tmp-1)/2;
        printf("%lld\n",ans);
    }
    return 0;
}

C1. Pokémon Army (easy version)

我用dp过的(而且状态也写得很复杂),这里也简单说一下,dp[i][0]表示以i结尾选择i未完成匹配的最大值,dp[i][1]表示以i结尾选择i完成匹配的最大值,dp[i]][2]以i结尾不选i的最大值。
状态转移方程如下。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
我们可以维护一个ma,为前i-1个dp[j][0]的最大值,就可以O(1)的更新dp[i][1]了。

#include <iostream>
#include <stdio.h>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const ll inf=(1ll<<50);
const int N=3e5+5;
ll dp[N][3];
int T,n,q;
ll a[N];
int main()
{
    while(~scanf("%d",&T))
    {
        while(T--)
        {
            scanf("%d%d",&n,&q);
            for(int i=1;i<=n;++i)
            {
                scanf("%lld",&a[i]);
                for(int j=0;j<3;++j)
                dp[i][j]=0;
            }
            ll ma=-inf;
            for(int i=1;i<=n;++i)
            {
                dp[i][0]=max(dp[i-1][1],dp[i-1][2])+a[i];
                dp[i][1]=ma-a[i];
                ma=max(ma,dp[i][0]);
                dp[i][2]=max(dp[i-1][1],dp[i-1][2]);
                //cout<<i<<" "<<dp[i][0]<<" "<<dp[i][1]<<" "<<dp[i][2]<<endl;
            }
            ma=-inf;
            for(int i=1;i<=n;++i)
            ma=max(ma,max(dp[i][0],max(dp[i][1],dp[i][2])));
            printf("%lld\n",ma);
        }
    }
    return 0;
}

C2. Pokémon Army (hard version)

其实这题不用dp,可以贪心的做。来看下图。
在这里插入图片描述
如果是这种情况,我们选加颜色的点为最优解。给出一个简单的证明,来看左边单调递减的部分。
这四个点记为a1,a2,a3,a4。其大小关系如下。

在这里插入图片描述
如果我们顺序选 a1-a2+a3-a4。不如选a1-a4。因为a1-a4+a3-a2<a1-a4(a3-a2<0)。其它情况显而也不如a1-a4。所以选峰和底是最好的。
hard版本,我们发现每次交换,只会改变自己,和左右两个位置,所以每次O(1)模拟改变一下即可。

#include <iostream>
#include <stdio.h>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
int T,n,q,l,r;
const ll N=3e5+5;
ll ans;
ll a[N];
void insert(int x)
{
    if(x<=0||x>n)
    return ;
    if(a[x-1]<a[x]&&a[x]>a[x+1])
    ans+=a[x];
    if(a[x-1]>a[x]&&a[x]<a[x+1])
    ans-=a[x];
}
void delet(int x)
{
    if(x<=0||x>n)
    return;
    if(a[x-1]<a[x]&&a[x]>a[x+1])
    ans-=a[x];
    if(a[x-1]>a[x]&&a[x]<a[x+1])
    ans+=a[x];
}
int main()
{
    while(~scanf("%d",&T))
    {
        while(T--)
    {
        ans=0;
        scanf("%d%d",&n,&q);
        a[0]=a[n+1]=-1;
        for(int i=1;i<=n;++i)
        {
            scanf("%lld",&a[i]);
            //insert(i);
        }
        for(int i=1;i<=n;++i)
        insert(i);
        printf("%lld\n",ans);
        while(q--)
        {
            scanf("%d%d",&l,&r);
            /*if(l==r)
            {
                printf("%lld\n",ans);
                continue;
            }
            */
            delet(l-1);
            delet(l);
            delet(l+1);
            if(r-1>l+1)
            delet(r-1);
            if(r>l+1)
            delet(r);
            if(r+1>l+1)
            delet(r+1);
            swap(a[l],a[r]);
            insert(l-1);
            insert(l);
            insert(l+1);
            if(r-1>l+1)
            insert(r-1);
            if(r>l+1)
            insert(r);
            if(r+1>l+1)
            insert(r+1);
            printf("%lld\n",ans);
        }
    }
    }
    return 0;
}

D. Rescue Nibel!

我们发现区间有1e9,但是n只有3e5。显然需要离散化,离散之后,遍历所有时间,记录每个时间点多了几个灯,少了几个灯。记录现在还有几个灯sum,遍历时间节点,对于每个时间节点,ans+=C(sum+in[i]-out[i],k)-C(sum-out[i],k)。减去的是之前算过的重复方案。组合数可以通过预处理O(1)的去计算。

#include <iostream>
#include <stdio.h>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N=3e5+5;
const ll mod=998244353;
int n,cnt;
ll k;
int l[N],r[N];
int C[N<<1];
int in[N<<1],out[N<<1];
ll A[N<<1],inv[N<<1];
long long quickpow(long long x,long long k)
{
    long long res=1;
    while(k)
    {
 	if(k&1)//k为奇数
 	res=res*x%mod;
 	k>>=1;//k/2
 	x=x*x%mod;
    }
     return res;
}
long long getC(ll n,ll m)
{
    //A[]为阶乘,inv[]为阶乘倒数
    if(n==m||!m)
    return 1;
    else
    return A[n]*inv[m]%mod*inv[n-m]%mod;
}
int main()
{
    //cout<<getC()
    scanf("%d%lld",&n,&k);
    //A[]为阶乘,inv[]为阶乘倒数
    A[0]=1;//0!=1
    for(int i=1;i<=n;i++)//n!=(n-1)!*n
    A[i]=A[i-1]*i%mod;
    //费马小定理
    inv[n]=quickpow(A[n],mod-2);//quickpow快速幂
    for(int i=n-1;i>=0;i--)//1/n!*n=1/(n-1)!
    inv[i]=inv[i+1]*(i+1)%mod;
    //cout<<inv[1]<<endl;
    //cout<<A[4]<<" "<<inv[3]<<" "<<getC(4,2)<<endl;
    for(int i=1;i<=n;++i)
    {
        scanf("%d%d",&l[i],&r[i]);
        C[++cnt]=l[i];
        C[++cnt]=r[i];
    }
    sort(C+1,C+1+cnt);
    int siz=unique(C+1,C+1+cnt)-C-1;
    ll sum=0;
    for(int i=1;i<=n;++i)
    {
        int pos=lower_bound(C+1,C+1+siz,l[i])-C;
        in[pos]++;
        pos=lower_bound(C+1,C+1+siz,r[i])-C;
        out[pos+1]++;
    }
    ll ans=0;
    for(int i=1;i<=siz;++i)
    {
        sum-=(long long)out[i];
        ll t1=0;
        if(sum>=k)
        t1=getC(sum,k);
        ll t2=0;
        sum+=(long long)in[i];
        if(sum>=k)
        t2=getC(sum,k);
        ans=(ans+t2-t1+mod)%mod;
        //cout<<in[i]<<" "<<out[i]<<" "<<sum<<" "<<t1<<" "<<t2<<endl;
    }
    printf("%lld\n",ans);
    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值