qzezoj 1568 反复求和
题面传送门
这道题首先要有一个思维的转换:对于选择满足\(0\leq i< n\) 的任意下标\(i\) ,并让\(a\)数组里下标为\(i\)处的值变为\(sum\)。把赋值的思维转化成选择一个下标,把除了这个下标的所有值加到它身上
这道题正着想很难想,俗话说得好,正难则反,所以我们不如倒着做
对于已经构造好的\(a\)序列,首先我们要确定它最后修改的位置。很明显,最后修改的位置是最大的,那么我们要计算它修改前的值,我们的思维转换就要派上用场了,其他值是不变的,所以我们维护一个总值,算出其他值,在这个数上减去就好了,一直到所有数都为\(1\)或有的数\(<1\)为止。
对于找最大值的过程,我们可以用一个大根堆维护。
#include<bits/stdc++.h>
using namespace std;
long long n,m,tot,a[100039],b[100039],sum,ans,pus,sss;
priority_queue<long long,vector<long long>,less<long long> > s;
int main(){
register int i;
scanf("%lld",&n);
for(i=1;i<=n;i++) scanf("%lld",&a[i]),s.push(a[i]),sum+=a[i];
for(i=1;i<=n;i++) if(a[i]==1) tot++;
while(tot!=n){
pus=s.top();
s.pop();
ans=sum-pus;
sum-=ans;
pus-=ans;
if(pus==1) tot++;
else if(pus<1) break;
else s.push(pus);
}
if(tot==n)printf("T");
else printf("N");
}
但这样有一个问题,有几组\(hack\)数据跑不过去。比如下面这组:
2
1 1000000000
我们发现重复减了很多次,我们可以计算出它要减几次,然后一并减掉。我们把这个元素拿出来,再取出一个堆顶的值,然后将他们两个作差后的结果除以减去的值向下取整再\(+1\),这就是要减的次数,用乘法一并减掉就可以了
AC代码:
#include<bits/stdc++.h>
using namespace std;
long long n,m,tot,a[100039],b[100039],sum,ans,pus,sss,now;
priority_queue<long long,vector<long long>,less<long long> > s;
int main(){
//freopen("1.in","r",stdin);
register int i;
scanf("%lld",&n);
for(i=1;i<=n;i++) scanf("%lld",&a[i]),sum+=a[i];
for(i=1;i<=n;i++) if(a[i]==1) tot++;
else s.push(a[i]);
//printf("%lld\n",tot);
while(tot!=n){
pus=s.top();
//printf("%lld\n",pus);
s.pop();
if(!s.size()){
if((pus-1)%(n-1)==0) tot=n;
break;
}
ans=sum-pus;
now=s.top();
sum-=((pus-now)/ans+1)*ans;
pus-=((pus-now)/ans+1)*ans;
if(pus==1) tot++;
else if(pus<1) break;
else s.push(pus);
}
if(tot==n)printf("T");
else printf("N");
}
还有,不开\(long long\)见祖宗啊!!!