BestCoder Round #62 (div.2)-Clarke and baton(二分搜索)

Clarke and baton

 
 Accepts: 1
 
 Submissions: 126
 Time Limit: 12000/6000 MS (Java/Others)
 
 Memory Limit: 524288/524288 K (Java/Others)
问题描述
克拉克是一名人格分裂患者。某一天,克拉克fork出了nn个自己,序号从11nn。
他们准备玩一个减肥游戏,每一个克拉克都有一个体重a[i]a[i]。他们有一个接力棒,这个接力棒任何时刻总是在最重的克拉克(如果重量相同则在序号最小的)的手中,得到这个接力棒的克拉克需要减肥,使得体重变成a[i]-1a[i]1,随后接力棒便传递到下一个人(可以是自己)的手中。
现在克拉克们知道接力棒一共被传递过qq次,他们想知道最终每一个克拉克的体重分别是多少。
输入描述
第一行一个整数T(1 \le T \le 10)T(1T10),表示数据的组数。
每组数据只有一行三个整数n, q, seed(1 \le n, q \le 10000000, \sum n \le 20000000, 1 \le seed \le 10^9+6)n,q,seed(1n,q10000000,n20000000,1seed109+6),分别表示克拉克的个数、接力棒传递次数还有一个随机种子。
a[i]a[i]按照以下规则生成:

long long seed;
int rand(int l, int r) {
	static long long mo=1e9+7, g=78125;
	return l+((seed*=g)%=mo)%(r-l+1);
}

int sum=rand(q, 10000000);
for(int i=1; i<=n; i++) {
	a[i]=rand(0, sum/(n-i+1));
	sum-=a[i];
}
a[rand(1, n)]+=sum;
输出描述
每组数据输出一行一个数,这个数ansans是这样得到的。
假设b[i]b[i]是最终克拉克们的体重,则ans=(b[1]+1) xor (b[2]+2) xor ... xor (b[n]+n)ans=(b[1]+1)xor(b[2]+2)xor...xor(b[n]+n),其中xorxor是异或操作。
输入样例
1
3 2 1
输出样例
20303
Hint
首先a[1]=20701, a[2]=31075, a[3]=26351a[1]=20701,a[2]=31075,a[3]=26351
第一次接力棒在22号克拉克手上,所以a[2]=31074a[2]=31074。
第二次接力棒在22号克拉克手上,所以a[2]=31073a[2]=31073。
所以答案等于(20701+1)xor(31073+2)xor(26351+3)=20303(20701+1)xor(31073+2)xor(26351+3)=20303

思路:
   这题非常的巧妙,一开始就想到了用优先队列来做,结果超时了。不过也是理所当然的事,不可能给你水过的,肯定卡你的时间。最后看了看别人的代码,看了好一会儿才明白他的原理,发现真的是巧妙啊,做出这题的人思维真是非常活跃,ORZ了。最后还是说说思路吧!这题其实就是要找到一个0-max的值中那个中间数值是能让输入的值减少q的,如果大了,就继续二分,直到L>R就停止。但是还是有一个bug,那就是当几个数非常接近时,就是你减一次,它减一次,它它再减一次。。。这样直到减完为止,这样就会出现一些减了一,一些没减1的情况,所以还要一个ans来记录有多少个是减了一的,其实它们不应该减的,再加回来就OK了。
 
 
AC代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<cmath>
#include<queue>
#include<vector>
typedef long long ll;
using namespace std;
#define T 15000005
#define inf 0x3f3f3f3f
ll seed;
ll a[T];
int n,q,val,ans;
int rand(int l, int r) {
	static long long mo=1e9+7, g=78125;
	return l+((seed*=g)%=mo)%(r-l+1);
}
//判断mid的值时有多少个是可以减去的
int jugde(int x)
{
	int sum=0;
	for(int i=1;i<=n;++i){
		if(a[i]>x){
			sum+=a[i]-x;
		}
	}
	return sum;
}
//二分符合减去q个数的那个mid值
void BS(int L,int R)
{
	int now;ans=inf;
	do{
		int mid = (L+R)>>1;
		now = jugde(mid);
		if(now>=q){
			val = mid,L = mid + 1;
		}
		else
		{
			R = mid - 1;
		}
		//判断是否出现有多个符合mid值却不需要减的数有多少个
		if(now>=q)ans = min(ans,now-q);
	}while(L<=R);
}
void solve()
{
	int maxn=0,i,j;
	for(i=1;i<=n;++i){
		maxn = max((ll)maxn,a[i]);
	}
	BS(0,maxn);
	//更新需要减少的数,还包括了不要减的数
	for(i=n;i;--i)if(a[i]>val)a[i]=val;
	//变回不用减的ans个数
	for(i=n;i;--i)if(ans>0&&a[i]==val)ans--,a[i]++;
	//计算总值
	int sum = 0;
	for(i=1;i<=n;++i)sum^=(a[i]+i);
	printf("%d\n",sum);
}
int main()
{
	#ifdef zsc
	freopen("input.txt","r",stdin);
	#endif
	int N;
	scanf("%d",&N);
	while(N--)
	{
		
		scanf("%d%d%lld",&n,&q,&seed);
		int sum=rand(q, 10000000);
		for(int i=1; i<=n; i++) {
			a[i]=rand(0, sum/(n-i+1));
			sum-=a[i];
		}
		 a[rand(1, n)]+=sum;
		 solve();
	}
    return 0;
}


 
 
TLE代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<cmath>
#include<queue>
#include<vector>
typedef long long ll;
using namespace std;
#define T 15000005
ll seed;
ll a[T];
struct node
{
    ll x,num;
    friend bool operator<(const node& a,const node& b){
        return a.x<b.x;
    }
}v;
int rand(int l, int r) {
    static long long mo=1e9+7, g=78125;
    return l+((seed*=g)%=mo)%(r-l+1);
}
int main()
{
    #ifdef zsc
    freopen("input.txt","r",stdin);
    #endif
    int N;
    scanf("%d",&N);
    while(N--)
    {
        int n,q;
        scanf("%d%d%lld",&n,&q,&seed);
        priority_queue<node> t;
        int sum=rand(q, 10000000);
        for(int i=1; i<=n; i++) {
            a[i]=rand(0, sum/(n-i+1));
            sum-=a[i];
        }
         a[rand(1, n)]+=sum;
        for(int i=1;i<=n;++i){
            v.x = a[i],v.num = i;
            t.push(v);
        }
        while(q--)
        {
            v=t.top();t.pop();
            /*printf("%d\n",v.x);*/
            v.x--;
            t.push(v);
        }
        node s = t.top();t.pop();
        ll ss=s.x+s.num;
        while(!t.empty())
        {
            s = t.top(),t.pop();
            ss^=(s.x+s.num);
        }
        printf("%lld\n",ss);
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值