问题描述
克拉克是一名人格分裂患者。某一天,克拉克fork出了nn个自己,序号从11到nn。
他们准备玩一个减肥游戏,每一个克拉克都有一个体重a[i]a[i]。他们有一个接力棒,这个接力棒任何时刻总是在最重的克拉克(如果重量相同则在序号最小的)的手中,得到这个接力棒的克拉克需要减肥,使得体重变成a[i]-1a[i]−1,随后接力棒便传递到下一个人(可以是自己)的手中。
现在克拉克们知道接力棒一共被传递过qq次,他们想知道最终每一个克拉克的体重分别是多少。
输入描述
第一行一个整数T(1 \le T \le 10)T(1≤T≤10),表示数据的组数。
每组数据只有一行三个整数n, q, seed(1 \le n, q \le 10000000, \sum n \le 20000000, 1 \le seed \le 10^9+6)n,q,seed(1≤n,q≤10000000,∑n≤20000000,1≤seed≤109+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;
}