Codeforces 731D 求所有区间的交(树状数组或前缀和)

题意:给你n个word,每个word都有若干个字母,现在要使得从上到下的word都必须小于或等于下一个word,你可以操作一次钥匙将所有word的所有字母都加上1,如果>c,就变为1,问是否有解,有的话随意输出,没有的话就输出-1;
思路:对于每两个上下相邻的word,去求出使得它们满足字典序的区间,再从0-c-1中找有没有点是n-1,也就是n-1个区间的共同交集。这里用树状数组实现,注意C开的范围和实际的范围是1e6+10和c。(因为树状数组求前缀和只能从1开始,所以求解的时候先所有都加1,最后再减掉1)。

#include
using namespace std;
#define mod 1000000007
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
typedef long long LL;
typedef unsigned long long ULL;

const int maxn=500050;
vector<int>v[maxn];
int n,c;

int C[1000010];//N表示输入的数组范围大小(1~N),
int lowbit(int x) //求最低位1的位置所表示的数
{
    return x&(-x);
}
void update(int p,int q)// 单点更新c[p] 加上q
{
                        //    (其中k为x二进制末尾0的个数)
    while(p<=c)
    {
        C[p]+=q;
        p+=lowbit(p);
    }
}
int S(int x)  //S(i)表示的是的前i个数的和
{
    int sum=0;
    while(x>0)
    {
        sum+=C[x];
        x-=lowbit(x);
    }
    return sum;
}
int ok;


int main()
{
    ok=0;
    scanf("%d%d",&n,&c);
    for(int i=1;i<=n;i++){
        v[i].clear();
        int cnt,t;
        scanf("%d",&cnt);
        while(cnt--){
            scanf("%d",&t);
            v[i].push_back(t);
        }
    }
    for(int i=1;iint p=0;
        while(1){
            if(p==v[i].size()){
                update(1,1);
                break;
            }
            else if(p==v[i+1].size()){
                ok=-1;
                break;
            }
            else if(v[i][p]!=v[i+1][p]){
                if(v[i][p]1][p]){
                    update(1,1);
                    update(c-v[i+1][p]+2,-1);
                    update(c-v[i][p]+2,1);
                    update(c+1,-1);
                }
                else if(v[i][p]>v[i+1][p]){
                    update(c-v[i][p]+2,1);
                    update(c-v[i+1][p]+2,-1);
                }
                break;
            }
            p++;
        }
        if(ok==-1)break;
    }
    if(ok==-1){printf("-1\n");return 0;}
    ok=-1;
    for(int i=1;i<=c;i++){
        if(S(i)==n-1){
            ok=i-1;break;
        }
    }
    printf("%d\n",ok);
    return 0;
}

后来发现好像没必要用树状数组,在区间两端位置放个++和–,然后求前缀和等于n-1的位置就好了。

#include
using namespace std;
#define mod 1000000007
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
typedef long long LL;
typedef unsigned long long ULL;

const int maxn=500050;
vector<int>v[maxn];
int n,c;
int C[1000010];//N表示输入的数组范围大小(1~N),
int ok;

int main()
{
    ok=0;
    scanf("%d%d",&n,&c);
    for(int i=1;i<=n;i++){
        v[i].clear();
        int cnt,t;
        scanf("%d",&cnt);
        while(cnt--){
            scanf("%d",&t);
            v[i].push_back(t);
        }
    }
    for(int i=1;iint p=0;
        while(1){
            if(p==v[i].size()){
                C[0]++;
                break;
            }
            else if(p==v[i+1].size()){
                ok=-1;
                break;
            }
            else if(v[i][p]!=v[i+1][p]){
                if(v[i][p]1][p]){
                    C[0]++;
                    C[c-v[i+1][p]+1]--;
                    C[c-v[i][p]+1]++;
                    C[c]--;
                }
                else if(v[i][p]>v[i+1][p]){
                    C[c-v[i][p]+1]++;
                    C[c-v[i+1][p]+1]--;
                }
                break;
            }
            p++;
        }
        if(ok==-1)break;
    }
    if(ok==-1){printf("-1\n");return 0;}
    ok=-1;
    int sum=0;
    for(int i=0;iif(sum==n-1){
            ok=i;break;
        }
    }
    printf("%d\n",ok);
    return 0;
}

你可能感兴趣的:(Codeforces 731D 求所有区间的交(树状数组或前缀和))