今天的比赛彻底自闭。。这道题跟原来做过的一道题很像,当时不会做,今天果然还是不会做,改了一下午都没改出来哇呜呜呜...果然我还是太菜了啊55555... 但是后来听说O(n^2)过了??exm???自闭了。。
那道类似的题是HDU4193,参考博客:https://blog.csdn.net/wiking__acm/article/details/7771134
HDU4193:循环序列,要往后补充n个数。若下标从1开始,对于位置i(i>=n)的数,如果以i-n+1为起点,i为终点,长度为n的这一段区间内的前缀和均非负,其实就是该前缀和区间内的最小值大于0。但一开始存的是从1开始的前缀和,为了求从i-n+1开始的前缀和,每次减掉sum[i-n]就可以。
本题思路:把点和邻接的边看作一个整体,权值为a[i]-b[i];再用一个前缀和sum,放到单调队列里。这道题与上题最大的不同就是多了一个初值c,使长度为n的这一段区间内前缀和均要满足+c>=0。
附上AC代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
#define ll long long
typedef pair<ll,ll>pp;
#define mkp make_pair
#define pb push_back
const double eps=1e-9;
const int INF=0x3f3f3f3f;
const int MAX=1e6+10;
ll n,c;
ll a[MAX];
ll b[MAX];
ll t[MAX*2];
ll sum[MAX*2];
deque<ll>q;//存下标
void ins(ll i)
{
while(!q.empty()&&sum[q.back()]>=sum[i])//找最小值的下标
q.pop_back();
q.pb(i);
while(!q.empty()&&i-q.front()>=n)//区间长度为n,注意有"="!
q.pop_front();
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%lld%lld",&n,&c);
for(ll i=1;i<=n;i++)
scanf("%lld",&a[i]);
for(ll i=1;i<=n;i++)
scanf("%lld",&b[i]);
while(!q.empty())//注意清空!
q.pop_back();
memset(sum,0,sizeof(sum));
memset(t,0,sizeof(t));
for(ll i=1;i<=n;i++)
{
t[i]=a[i]-b[i];
t[i+n]=t[i];
}
for(ll i=1;i<=2*n;i++)
sum[i]=sum[i-1]+t[i];
ll i;
for(i=1;i<n;i++)//不可少!
ins(i);
for(i=n;i<2*n;i++)
{
ins(i);
/* q.front()存区间内前缀和最小值的下标;
-sum[i-n]变成从i-n+1开始的区间内的前缀和;
+c使满足题目条件 */
if(c+(sum[q.front()]-sum[i-n])>=0)
break;
}
if(i==2*n)
printf("-1\n");
else
printf("%d\n",i-n+1);
}
return 0;
}
论补题的重要性。。。