题意
n支队伍,一张桌子有m个座位顺时针编号1~m。p个预言。预言a队伍在b时刻ac一道题目。机器人每个时刻都顺时针走一步,有需要气球的就发。每支队伍,ac后多少时刻没收到气球就有多少不开心的值,问机器人起始位置在哪里可以使得总的不高兴值最小。输出最小的不高兴值。
题解
n 1e5 m 1e9 ,robot的可能出发位置一定是在这1e5个之内,就是当它到某个队伍可以恰好给这个队伍气球的时候,这个位置一定是其中之一。
可以将所有队伍的AC时间等价到一支队伍上,例如第一支队伍位置p1,A题时间为a,第二支队伍位置是p2,A题时间为b,那么其实可以转化为第一支队伍AC另外一道题的时间是 b-(p2-p1),这里注意考虑取模!
假设机器人从第一支队伍的位置出发,当b不为0时,那么每次答案都需要加上(m-b) b为A题的时间。//绕一圈回来 等待时间+(m-b),对于假设从pos[1]出发的ans求解O(n),如果对每个位置都这样求->O(n^2)炸了呜呜呜。
所以对于这n个可能的起始位置,我们可以做到每次O(1)转移得到相应答案 再取min,怎么转移呢?
对于前面求出的每一个b(映射的AC时间)从小到大枚举,->机器人起点变化
ans += p * (现在的位置 - 上一个);//所有点都加上这一段的时间
ans -= m * B;//刚好在这儿的点减掉一个轮回
答案取min,nice~
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<map>
#include<cmath>
using namespace std;
const int MaxN = 1e5 + 5;
typedef long long LL;
int t,a,b,n;
LL m,p;
int pos[MaxN];
LL ans = 0LL;
map<int,int>mp;
int main(){
scanf("%d",&t);
while(t--){
ans = 0LL;
mp.clear();
scanf("%d %lld %lld",&n,&m,&p);
for(int i = 1;i <= n; i++) scanf("%d",&pos[i]);
for(int i = 1;i <= p; i++){
scanf("%d %d",&a,&b);
b = b - ((pos[a] - pos[1]) + m) % m;
b = (b + m) % m;
mp[b]++;
if(b) ans += m - b;
}
LL ANS = ans;
int lst = 0;
for(auto it = mp.begin();it != mp.end(); it++){
LL de = it->first - lst;
if(!de) continue;
ans += p * de;
ans -= it->second * m;
ANS = min(ANS,ans);
lst = it->first;
}
printf("%lld\n",ANS);
}
return 0;
}