AtCoder Grand Contest 023

链接

D. Go Home

考虑两端的最后一组到的人,如果左边的人数大于等于右边,那么右边的人只能希望左边的人快点到,所以他们会和左边的人投一样的票。然后就可以把这组人加到左边去,这样就变成了一个子问题。

最后全部在公司一侧了就直接开过去即可。复杂度 \(O(n)\)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100010
#define ll long long
using namespace std;
int x[N],n,s;
ll p[N];
int main()
{
    scanf("%d%d",&n,&s);
    for(int i=1;i<=n;i++) scanf("%d%lld",&x[i],&p[i]);
    int l=1,r=n,op=p[1]>=p[n];
    ll ans=0;
    while(l<=r)
    {
        if(x[l]>s){ans+=x[r]-s;break;}
        if(x[r]<s){ans+=s-x[l];break;}
        if(p[l]>=p[r]) p[l]+=p[r],ans+=op*(x[r]-x[l]),op=0,r--;
        else p[r]+=p[l],ans+=(1-op)*(x[r]-x[l]),op=1,l++;
    }
    printf("%lld\n",ans);
    return 0;
}

E. Inversions

首先考虑 \(O(n^2)\) 怎么做:

考虑算原序列的一种方法:用 \(d_i'\) 表示 \(a_i\geq i\) 的数量,那么 \(i\) 可以放在这 \(d_i'\) 个位置上,但其中恰好有 \(n-i\) 个位置已经放了数字,所以总共还剩 \(d_i'-(n-i)\) 个位置。令 \(d_i=d_i'-(n-i),M=\prod d_i\)

然后考虑将逆序对分开处理贡献,即考虑 \(i< j,p_i< p_j\) 的方案数。

不妨钦定此时 \(a_i< a_j\),那么要产生逆序对当且仅当 \(j\) 位置的值比 \(i\) 小,可以发现这部分等价于 \(a_i=a_j\),即逆序对数等于总数除以二。但同时所有位于 \((a_i,a_j]\) 中的数字都会少 \(1\) 的答案,所以总方案数就是 \(M\prod \frac{d_i-1}{d_i}\)

对于 \(a_i>a_j\) 的情况,可以发现就是总方案减去上述方案。

直接枚举 \(i,j\) 就是 \(O(n^2)\)。考虑优化。

可以发现枚举 \(i\),总方案数本质上是一些后缀积的和,所以直接用树状数组维护即可。复杂度 \(O(n\log n)\)

注意特判 0。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=200010,mod=1000000007,iv2=(mod+1)/2;
int n;
int ksm(int a,int b=mod-2)
{
    int r=1;
    for(;b;b>>=1,a=1ll*a*a%mod) if(b&1) r=1ll*r*a%mod;
    return r;
}
struct tree_a{
    int a[N];
    void clear(){memset(a,0,sizeof(a));}
    void add(int x,int v){for(;x<=n;x+=(x&-x)) a[x]=(a[x]+v)%mod;}
    int qry(int x){int v=0;for(;x;x-=(x&-x)) v=(v+a[x])%mod;return v;}
    int sum(int l,int r){return (qry(r)-qry(l-1)+mod)%mod;}
}c0,c1;
int a[N],p[N],M=1;
int pre[N],sum[N];
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]),p[a[i]]++;
    for(int i=n-1;i;i--) p[i]+=p[i+1];
    for(int i=1;i<=n;i++) p[i]-=n-i,M=1ll*M*p[i]%mod;
    sum[0]=pre[0]=1;
    for(int i=1;i<=n;i++)
    {
        if(p[i]<=1) pre[i]=i,sum[i]=sum[i-1];
        else pre[i]=pre[i-1],sum[i]=1ll*sum[i-1]*(p[i]-1)%mod*ksm(p[i])%mod;
    }
    int ans=0;
    for(int i=1;i<=n;i++)
    {
        ans=(ans+1ll*c0.sum(pre[a[i]],a[i])*sum[a[i]]%mod*iv2%mod)%mod;
        c0.add(a[i],ksm(sum[a[i]]));
    }
    c0.clear();
    for(int i=n;i;i--)
    {
        ans=(ans+c1.qry(a[i]-1)-1ll*c0.sum(pre[a[i]],a[i]-1)*sum[a[i]]%mod*iv2%mod+mod)%mod;
        c0.add(a[i],ksm(sum[a[i]]));c1.add(a[i],1);
    }
    ans=1ll*ans*M%mod;
    printf("%d\n",ans);
    return 0;
}

F. 01 on Tree

题解

posted @ 2023-09-20 18:58  Flying2018  阅读(14)  评论(0)    收藏  举报