CF956(div2) EF题解

Problem - E - Codeforces

E. I Love Balls

显然两个人的得分期望加起来等于全部得分总和,于是我们只用算一个就行了,那就算Alice的吧,假如没有特殊球,记sum为普通球的总分数,那么Alice的得分为:

n为偶数:那么Alice得到一半的球那么期望就是sum/2

n为奇数Alice得到n/2+1个球,期望就是(n/2+1)/n*sum

在考虑特殊球,假如我们把球被取出来的顺序当成一个排列,那么原本就是n-k个普通球的排列,而特殊球要随机的出现在n-k+1个空位当中,我们可以惊奇的发现特殊球的出现对普通球的归属并不影响,原本第奇数个普通球是Alice的,出现特殊球后第奇数个球仍然是Alice的,举个例子,第一个普通球是Alice的,第二个是Bob的,那么如果在他们中间出现任意多个特殊球,最后第二个球还是Bob的,所以特殊球对普通球所产生的贡献并不影响,因此可以单独考虑两者的贡献,普通球的贡献上面已经算过了接下来考虑特殊球的贡献

特殊球是随机出现在那n-k+1个空位里面的,而且出现在奇数位前面的特殊球一定是Alice的,例如出现在第一个普通球或第三个普通球的前面一定都被Alice所得,记特殊球的总分为sum2,那么Alice的特殊球期望得分就是(奇数位的个数)/(n-k+1)*sum2,总期望两者加起来即可

附上代码(相当简短)

#include<iostream>
using namespace std;
#define int long long
const int mod=1e9+7;
int n,m,k,d,a[400010];
int qsm(int a,int b)
{
	int res=1;
	while(b)
	{
		if(b&1)res=res*a%mod;
		a=a*a%mod;
		b=b>>1;
	}
	return res;
}
void solve()
{
	int sum1=0,sum2=0;
	cin>>n>>k;
	int m=n-k+1;
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=1;i<=k;i++)sum2+=a[i];
	for(int i=k+1;i<=n;i++)sum1+=a[i];
	int res1=sum1*(m/2)%mod*qsm(n-k,mod-2)%mod;//普通球的贡献
	int res2=sum2*((m+1)/2)%mod*qsm(m,mod-2)%mod;//特殊球的贡献
	cout<<(res1+res2)%mod<<' '<<((sum1+sum2-res1-res2)%mod+mod)%mod<<endl;
}
signed main()
{
    cin.tie(0), cout.tie(0), ios::sync_with_stdio(false);
    int T;cin>>T;
    while(T--)solve();
    return 0;
}

Problem - F - Codeforces

F. array-value

二分+异或字典tire

看到第k大,并且k是1e10的范围那么肯定是不支持线性的,那么就尝试二分,二分一个答案x,看区间价值小于等于的区间个数是否小于k,那么这个区间个数怎么求呢,我们可以发现如果r固定,那么那么区间价值一定随着左端点l的减小减小的,那么就有单调性,那么必然存在一个点记为f[r],使得,左端点在[1,f[r]],右端点为r的区间的价值全部小于x,对于这个f[r],我们可以用01字典tire来找,具体怎么找这个还是有点不太好理解的,等下会写在代码注释里,这里不过多叙述,但是我们的维护一个01前缀的最大位置(代表最后的位置),那么我们每次二分的只需比较所有f[i]的总和和k就好了,f[i]的总和就是比区间价值小于等于x的区间数

附上代码:

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5*40+114;
int id[N];
int tr[N][2],idx;
int n,a[N],rt;
int dp[N];
long long k;
void insert(int x,int y)
{
    int p = 1;
    for (int i = 30; i >= 0; i -- )
    {
        int &s = tr[p][x >> i & 1];
        if (!s) s = ++ idx;
        p = s;
        id[p]=max(id[p],y);
        //假如有两个数10011010 在数组a中的位置是4,10010111在数组中的位置是5,那么1001这个前缀的id就是5,取最靠后的位置
        //想必有了这个例子应当可以明白id数组是什么意思了
    }
}
int query(int x,int y)
{
	int p=1;
	int res=0;
	for(int i=30;i>=0;i--)
	{
		if(y>>i&1)
		{
			if(x>>i&1)//如果两个数在这一位都为1
			{
				res=max(res,id[tr[p][1]]);//直接给y或上一个1,那么就不用往下走下去了,直接算答案
				p=tr[p][0];//给y或上一个0,那么x和y这一位还是一,无法比出大小,所以要继续走
			}
			else{
				p=tr[p][1];//如果这一位y是1,x是0那么必需给y或上一个1,不然y就比x大了,并往下走,继续比较
			}
		}
		else
		{
			if(x>>i&1)//这一位y为0,x为1
			{
				res=max(res,id[tr[p][0]]);//直接给y或上一个0,那么y就比x小了,后面不用比了,直接算答案
				p=tr[p][1];//给y或上一个1,两者相等继续比下去
			}
			else{
				p=tr[p][0];//两者都为0,或上一个0,继续比
			}
		}
	}
	res=max(res,id[p]);
	return res;
}
bool check(int x)
{
	++idx;
	long long sum=0;
	for(int i=1;i<=n;i++)//这里每次二分都要将字典树重新建一遍,或者用可持久化字典tire
	{
		dp[i]=query(x,a[i]);//查找和a[i]异或起来小于等于x的数的最后一个位置在哪
		insert(a[i],i);//将a[i]插入字典树中
	}
	for(int i=1;i<=n;i++)dp[i]=max(dp[i-1],dp[i]);//之前算的dp[i]只是a[i]自己,要让整个区间合法,必然是之前的区间的max
	for(int i=1;i<=n;i++)sum+=dp[i];
	for(int i=0;i<=idx;i++)tr[i][0]=tr[i][1]=id[i]=0;//清空字典树
	idx=0;
	if(sum>=k)return true;
	return false;
}
void solve()
{
	cin>>n>>k;
        for(int i=1;i<=n;i++){
            cin>>a[i];
        }
        long long l=-1,r=2e9+114;
        while(l<r){
            int mid=(l+r)>>1;
            if(check(mid)==true) r=mid;
            else l=mid+1;
        }
        cout<<r<<endl;
}
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int T;
    cin>>T;
    while(T--)solve();
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值