题目链接:http://hihocoder.com/problemset/problem/1831
题目大意:
世界各地的一个圆圈上有n个城市,它们按照圆圈上的顺序从1到n编号。当你到达这个城市会得到ai块钱,从这个城市去另外的城市要花费bi块钱,这个游戏的目标是选择一个城市作为起点,一开始你有c美元,然后沿着圆圈去一次访问整个城市,最后回到起点。在旅途中,你的钱必须不低于零。
这里有一个问题:要完成这次旅行,您会选择哪个城市作为开始城市?
如果有多个答案,请输出编号最小的答案。
如果没有答案, 输出-1

输入的第一行是一个整数T(T ≤100),的测试案例数量。
对于每一个测试的情况下,第一行包含两个整数N和C(1≤N ≤10^6, 0≤ C≤10^9)。
第二行包含N个整数ai(- 10^9 ≤ ai ≤10^9),
第三行包含N个整数bi…bn(0≤ bn ≤10^9)。

保证所有测试用例中n的总和小于10^6

思考:

因为到一个城市先得到钱,所以经过一个城市得到的钱为a[i]-b[i];
图中ai=a[i]-b[i];

首先求前缀和,判断s[n]+m>=0,如果成立则可以访问全部城市。
这时枚举起始城市,如果以1为开始城市,则是s[i]+m,…,s[n]+m都要>=0。
如果以2为开始城市,则以2为起始城市的n个城市的前缀和都要>=0。

以此类推

如果每枚举一个起点,求一次前缀和,复杂度为O(T*n^n),肯定会T。

那么我们可以动态维护前缀和。
假如以2为起点时,除a1的前缀和,其他的前缀和都-=a1。而s[1]=(a[1]-b[1])+s[n-1];
假如以3为起点时,除a2的前缀和,其他的前缀和都在以2为起点的基础上-=a2。而s[2]=(a[2]-b[2])+s[1];

以此类推

那么所有的-=可以用s3记录下来,那么每次只要维护s[i-1]就行了。
用multiset存储前缀和,那么每次找min前缀和的复杂度为log n。
整体的复杂度为O(T* n*log n)

思考:当时T了很多次,后来发现是数组开小了。。。。。,话说怎么不是MLE。。。。
因为T了很多次,当时以为O(T* n*log n)的f’z’d过不了,还想到一种O(T *n)的思路,不过没有验证过是否正确。

记录第一次的前缀和的最小值S(min),和下标j,在每次枚举起点i时,除S[i-1]外,所有的前缀和都减去相同的数,所以S(min)=min( S(min), S[i-1] )

如果S[min]的下标为j, 则所以的枚举i<=j都没使S[min]-s3+m>=0, 则起点为j+1(没有证明)

刚才再看了当时的代码,发现维护前缀和的地方,维护的是S[i]的前缀和,竟然AC。。。。。数据太水???

#include<bits/stdc++.h>
using namespace std;

int a[1000005];
int b[1000005];
long long s[1000005];


int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        multiset<long long> st;
        int n, m;
        scanf("%d%d",&n,&m);
        for(int i=0;i<n;i++)
        {
            scanf("%d",&a[i]);
        }
        for(int i=0;i<n;i++)
        {
            scanf("%d",&b[i]);
        }
        s[0]=a[0]-b[0];
        long long s1=s[0];
        for(int i=0;i<n;i++)
        {
            s[i]=s[i-1]+a[i]-b[i];//前缀和
            st.insert(s[i]);
        }
        s1=s[n-1]+m;
        if(s1>=0)//如果成立则可以访问全部城市。
        {
            long long s2=*st.begin();//前缀和的最小值
            long long s3=0;
            if(s2+m>=0)              //第一个城市
            {
                printf("1\n");
            }
            else
            {
                for(int i=1;i<n;i++)
                {
                    s3+=(a[i-1]-b[i-1]);//记录s3
                    st.erase(st.find(s[i-1]));
                    if(i==1)
                    {
                        s[0]=(a[0]-b[0])+s[n-1];//i="2"特殊维护
                        st.insert(s[0]);
                    }
                    else
                    {
                        s[i-1]=(a[i-1]-b[i-1])+s[i-2];//维护前缀和
                        st.insert(s[i-1]);
                        /*
                        写成
                        s[i]=(a[i]-b[i])+s[i-1];
                        st.insert(s[i]);
                        也能AC
                        数据比较水???
                        */
                    }

                    s2=*st.begin();
                    if(s2+m>=s3)//判断是否满足条件
                    {
                        printf("%d\n",i+1);
                        break;
                    }
                }
            }
        }
        else
            printf("-1\n");
    }

    return 0;
}