http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3981
这个重现赛我真的是坚持到最后20分钟还在写题,虽然最后A题也没过吧,但是总归是写到最后了,希望日后如果现场做比赛也有这淡定!
A题是看着长一些,但是我读了几遍之后就懂了题意,然后又想了一下,感觉思路还挺对的,也不像是啥算法题,此时距离比赛结束还有近两个小时(总不能一直挂机)…就敲了一下,结果超时了,不是很懂为什么超时…比赛结束之后看了一下题解,发现…好像不是像我这样想的(哀嚎啊),但是我觉得我还是可以想想搞定的!
#include
using namespace std;
#include
#include
#include
typedef struct Pre
{
long long a,b;
};
Pre pree[100005];
long long n,m,p;
set<long long>ACtime[100005];
long long pos[100005];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
long long num[100005];
memset(num,0,sizeof(num));
scanf("%lld%lld%lld",&n,&m,&p);
int i;
for(i=1;i<=n;i++)
scanf("%lld",&pos[i]);
for(i=1;i<=n;i++)
ACtime[i].clear();
for(i=0;ilong long a,b;
scanf("%lld%lld",&a,&b);
int pnum=ACtime[a].size();
ACtime[a].insert(b);
if(ACtime[a].size()>pnum)
{
num[a]+=b;
}
pree[i].a=a;
pree[i].b=b%m;
}
for(i=1;i<=n;i++)
num[i]=num[i]%m;
/*for(i=1;i<=n;i++)
{
long long sum=0;
for(set::iterator it=ACtime[i].begin();it!=ACtime[i].end();it++)
sum+=*it;
sum=sum%m;
num[i]=sum;
}*/
int difficultypos;
int mmax=-1;
for(i=1;i<=n;i++)
{
if(mmaxint dmin=*ACtime[difficultypos].begin();
dmin=dmin%m;
int start=(pos[difficultypos]-dmin+1+m)%m;//初始位置
//cout<<"初始位置:"<
long long arrive[100005];
for(i=1;i<=n;i++)
{
arrive[i]=(pos[i]-start+1+m)%m;
//cout<
}
//cout<
long long ans=0;
for(i=0;i//ans+=abs(arrive[pree[i].a]-pree[i].b);
if(arrive[pree[i].a]>=pree[i].b)
ans+=arrive[pree[i].a]-pree[i].b;
else
ans=ans+arrive[pree[i].a]-pree[i].b+m;
}
printf("%lld\n",ans);
}
return 0;
}
☝这是我超时的代码
题意:n,m,p三个数字(10^5,10^9,10^5)分别代表n个参赛队伍,m个座位,p条预言,一个机器人从m个位置任意出发,然后每秒钟顺时针移动一个格(到m之后下一个就会去1),然后走到哪里就会把气球发给已经AC的队伍,假设a队伍b时刻解决了一题,结果c时刻拿到气球,那么他们的不开心值就是c-b,要求总的不开心值最小,然后确定机器人的初始位置,进而确定最小的不开心值。
我的思路:位置有10^9个,应该要避开这个,但是参赛队伍的数目和预言的数目都是10^5,都是可以遍历的,经过我对样例的分析(天真,围笑),机器人初始的位置就在那个”AC最多”的队伍处(后来发现都是臆想!)就是把每一个队伍解决的题目的不同的时间(因为相同的就可以一起解决)相加,然后对m取模,排序之后最大的那个,保证它生气值最小,从而确定机器人的初始位置。
看了好多题解,加自己又想了一下,发现我的想法有很多错误之处。(以下纯属瞎扯,估计过段时间自己也看不懂了)
①并不能保证我找到起点就是最优的起点,因为哪怕它是最多的,取它做初始也不能保证它最终用时最少。
②相同的并成一样的解决这个想法就很有问题,因为虽然可以一起解决,但是不开心值也是按照倍数增加的。
下面说一下新的想法:
解决这个题最重要的是找到初始位置,既然已经不能O(1)确定初始位置(其实想想也是,如果能够一下子就找到初始位置,设置这么大的数据的意义在哪里呢,所以这就启示我们,该遍历的时候要遍历,不要随意地下结论,就好像是不知道全部不要瞎逼逼一样,个人感觉ACM大部分题目还是要遍历的,多了解才有发言权),那么就要遍历,位置有10^9个,所以不能遍历,那么就是可以使某些题目的不开心值为0的位置才是我们需要遍历的。
解释一下:
可以把机器人走的路线想成一条直线而不是一个环,那么当他处在一个不存在队伍AC题目的时间时,所要做的就是尽快到达下一个地方,在他后方的就是在他后方,在他前方的可以想象成在他后方,自然是到达那个离他最近的地方为0的地方(不然在路上瞎耽误什么工夫),emmmm还是说不清楚…
那么问题又来了,遍历这些位置就需要10^5,确定这些位置的不开心值只能是O(1)的复杂度,那怎么确定呢?
可以发现,机器人的初始位置每向后移动一下,原来为0的变为m-1,原来不为零的减一,这个还是把机器人走的路想成一条线,就会好理解一些。那么我们只需要求出当起始位置为1的时候的总的不开心值,然后以一个O(1)的复杂度的表达式就可以确定每一个初始位置的不开心值。有一点像逆序数那个的,都是先确定一个最初的状态,然后后面的状态都是这个状态的线性的改变。
所以这个题目的难点就在于:
①理解怎么确定起始位置
②确定好起始位置之后怎么求不开心值
#include
using namespace std;
#include
long long pos[100005];
long long truestart[100005];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
long long n,m,p;
long long sum=0;
scanf("%lld%lld%lld",&n,&m,&p);
int i;
for(i=1;i<=n;i++)
scanf("%lld",&pos[i]);
for(i=1;i<=p;i++)
{
int a,b;
scanf("%d%d",&a,&b);
//truestart[i]=(pos[a]+1-b%m+m)%m;
int temp=(pos[a]+1-b%m+m)%m;
if(!temp)
truestart[i]=m;
else
truestart[i]=temp;
cout<1)%m;
}
//cout<
sort(truestart+1,truestart+p+1);
long long ans=9999999999;
for(i=1;i<=p;i++)
{
ans=min(ans,sum+(i)*m-p*truestart[i]);
}
printf("%lld\n",ans);
}
return 0;
}
乱七八糟的一个代码