ZOJ 3981 Balloon Robot CCPC2017 Qinhuangdao(推公式+离线处理)_CCPC



ZOJ 3981 Balloon Robot CCPC2017 Qinhuangdao(推公式+离线处理)_c++_02


        现在回想起来,真的不应该去用C++强撸那道应该用Java做的大整数的题目了……


        大致题意就是,有一个发气球的机器,然后有n个人,m个座位排成了一个圆圈,总共AC了p道题目,然后机器人每次从某一个位置开始移动,每次花1个时间单位移动一格。每次机器人走到一个人旁边,它就会把这个人已经A的所有题目对应的气球发给他。对于每一道题目,产生的不高兴值是发气球的时间,减去对应A题的时间。然后问,如何选取这个出发点使得总的不高兴值最大。


        首先,我们考虑如果发气球机器的起始位置是x,那么对于一道题目,假设解题时间是t,解题人位置是pos,那么其不高兴值是(pos-t)%m-x,这里要注意,如果最后结果小于0,那么要对应加上一个m。那么对于所有的题目,就是对这个单个的式子求个和。现在要求是不高兴值的和最小,那么我们完全可以枚举这个x,然后对应求出每一个和,找到最小的即可。这里很容易证明,x只需要枚举那些可以使得某些题目不高兴值为0的位置。


        那么现在问题变成了已知x,如何在O(1)的时间内求出不高兴值的和呢?根据表达式pos-t-x,我们可以发现对于每一个问题pos-t是不变的,因此我们可以提前统计出pos-t的和,用和再减去x乘以A题个数即可。然而,并没有想的那么简单,因为如果说pos-t小于x,那么还要再等一个周期才能发到气球,即不高兴值得再加上m。所以说,每当枚举到一个x,我们还要统计出pos-t的值比当前x小的个数,在最后结果上加上个数乘以m才是当前x对应的不高兴值。至于统计这个,我们排序之后,按顺序离线处理,就可以做到均摊的O(1)的复杂度。总共的复杂度由于排序的存在,是O(NlogN)。代码简短(下次再也不去写c++的大整数了……)具体见代码:


#include<bits/stdc++.h>
#define LL long long
#define N 100010
using namespace std;

int n,m,p,tot,pos[N],a[N],b[N];

int main()
{
int T_T;
cin>>T_T;
while(T_T--)
{
LL ans=1e18,s=0;
scanf("%d%d%d",&n,&m,&p);
for(int i=1;i<=n;i++)
scanf("%d",&pos[i]);
for(int i=1;i<=p;i++)
{
int x,y;
scanf("%d%d",&x,&y);
a[i]=(pos[x]-y+m)%m; //保证a[i]要大于等于0
b[i]=a[i]%m; s+=a[i];
}
sort(a+1,a+1+p);
sort(b+1,b+1+p);
tot=unique(b+1,b+1+p)-b-1;
for(int i=1,j=1;i<=tot;i++)
{
while(a[j]<b[i]&&j<p) j++;
ans=min(ans,s+(LL)(j-1)*m-(LL)p*b[i]);
}
printf("%lld\n",ans);
}
return 0;
}