2018ICPC青岛I Soldier Game (线段树)

题目要求的是分组后的组间最大值-最小值的最小是多少。

因为我们发现有两个变量,因此很难做,一种经典思路就是固定一个答案,然后求相对于这个答案的另一个值的最优解,枚举所有情况取min即可。

对于本题,我们发现所有的值其实已经明了,因此先预处理计算后,我们可以枚举最小值,之后找到满足条件的最小的最大值。

所谓满足条件就是找到一种不相互覆盖的方式能够覆盖整个1-n区间

区间覆盖问题可以使用线段树来维护,但是这里要做一点特殊的处理,因为我们有长度为1的边和长度为2的边。因此我们维护的信息是tr[u][2][2],分别表示这个l,r   l+1,r   l,r+1 l+1,r+1

在pushup的时候用不重复的更新方式去更新,就能得到所有对于能够覆盖区间的方案。

现在有一点问题是,对于我的做法如何能保证取到的最小值和最大值一定能组成合法方案呢?

答案是不一定能组成合法方案,但是组不成的不会是最后的答案,所有统统更新即可。

对于细节上,我们可以简单讨论证明。

合法方案不必多说,直接去min

对于其中可能存在的非法方案,也就是你当前最小值点,可以被其他区间替换,并且他们也能做到覆盖整个区间,那么当前最小值点是非法的。

例如 2 4 5 7,那么我们发现去掉这个最小值,我们仍然得到一个更优的并且能满足题意要求的答案,因此当前答案不会成为最后答案,所以直接更新即可

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pll;
const int N=2e5+10;
pll p[N<<2];
int a[N][2];
int tr[N<<2][2][2];
int n;
bool cmp(pll x,pll y){
    return a[x.first][x.second]<a[y.first][y.second];
}
void build(int u,int l,int r){
    tr[u][0][0]=tr[u][1][0]=tr[u][1][1]=tr[u][0][1];
    if(l==r){
        tr[u][1][0]=1;
        return ;
    }
    int mid=l+r>>1;
    build(u<<1,l,mid);
    build(u<<1|1,mid+1,r);
}
void pushup(int rt){
    tr[rt][0][0] = (tr[rt<<1][0][0] && tr[rt<<1|1][0][0]) || (tr[rt<<1][0][1] && tr[rt<<1|1][1][0]);
    tr[rt][0][1] = (tr[rt<<1][0][0] && tr[rt<<1|1][0][1]) || (tr[rt<<1][0][1] && tr[rt<<1|1][1][1]);
    tr[rt][1][0] = (tr[rt<<1][1][0] && tr[rt<<1|1][0][0]) || (tr[rt<<1][1][1] && tr[rt<<1|1][1][0]);
    tr[rt][1][1] = (tr[rt<<1][1][1] && tr[rt<<1|1][1][1]) || (tr[rt<<1][1][0] && tr[rt<<1|1][0][1]);
}
void modify(int u,int l,int x,int L,int R){
    if(L==R){
        tr[u][0][x]^=1;
        return ;
    }
    int mid=L+R>>1;
    if(l<=mid)
        modify(u<<1,l,x,L,mid);
    else
        modify(u<<1|1,l,x,mid+1,R);
    pushup(u);
}
int main(){
    ios::sync_with_stdio(false);
    int t;
    cin>>t;
    while(t--){
        int cnt=0;
        cin>>n;
        int i;
        for(i=1;i<=n;i++){
            cin>>a[i][0];
            p[++cnt]={i,0};
            if(i-1){
                a[i-1][1]=a[i-1][0]+a[i][0];
                p[++cnt]={i-1,1};
            }
        }
        sort(p+1,p+cnt+1,cmp);
        build(1,1,n);
        int j;
        ll ans=1e18;
        for(i=1,j=1;i<=cnt;i++){
            while(j<=cnt&&!tr[1][0][0]){
                modify(1,p[j].first,p[j].second,1,n);
                j++;
            }
            if(tr[1][0][0]){
                ans=min(ans,1ll*a[p[j-1].first][p[j-1].second]-1ll*a[p[i].first][p[i].second]);
            }
            modify(1,p[i].first,p[i].second,1,n);
        }
        cout<<ans<<endl;
    }
    return 0;
}
View Code

 

posted @ 2020-10-22 18:01  朝暮不思  阅读(179)  评论(0编辑  收藏  举报