7.16 T3 奇袭

题意:n*n的一张图,每行每列保证有且仅有一个军队,如果有一个k×k的区域里面恰有k个军队,危险程度就+1,求这张图的危险程度。对于30%的数据,N ≤ 100;对于60%的数据,N ≤ 5000;对于100%的数据,N ≤ 50000

题解:因为每行每列保证有且仅有一个军队,所以可以把整张图“拍扁”,a[x][y]=1-->a[x]=y;n方的复杂度这题抗不住,听说减减枝可以卡到91,之后就卡不动了。。

正解是分治或线段树,我打的是分治。

对于一个区间,可以对答案做贡献的条件就是序列是连续的(但顺序可以是乱的),即max-min=k-1。这个区间对答案的贡献可以分为3部分,mid左边,mid右边,横跨mid。mid左右的直接分治就行,关键就在与横跨mid的那部分。

横跨mid又可以分为4中情况max min都在mid左,max min都在mid右,min在mid左max在mid右,max在mid左min在mid右。发现1 2,3 4是对称的,所以只说一种。

max min都在mid左:lmax[l]-lmin[l]+1=r-l+1 发现枚举左端点就可以得到右端点,判断一下右端点是否合法就可以了;

min在mid左max在mid右:rmax[R]-lmin[L]=R-L 移项得rmax[R]-R=lmin[L]-L 用桶存符合情况rmax[R]-R,用左边拿出来就行;判是否符合情况的思想有点类似与单调队列的思想,rmax等数组存的是前缀max或min,具有单调性。注意判R的时候只能用min卡,因为如果rmax[R]>lmax[L],那么a[R]<lmax[l]这种情况在rmax中没有体现,但他却是不合法的;用rmin[R]>lmin[l]的话,一旦a[R]<lmin[l],rmin[R]中也会体现,可以卡住。L同理。

枚举左端点,越往右走lmin越大,lmax越小,就要求R的rmin增大已使继续满足lmin<rmin 所以让R左移同时排除不符合的情况,而对L的rmax的限制就有所放宽,可以稍小一些,所以L也左移,同时把符合的放进去。最后用ans+=t[lmin[L]-L]。值得注意的是,桶里的个数可能是负的,即没有符合的情况,所以加ans的时候需要判一下是否>0。而负的对以后更新没有任何影响,不用管他就行了。

桶的下标可能会出负数,统一+n就可以避免这种情况了。

清空桶的时候不要用memset,因为太慢了,还是手清吧。。

再一个卡我的地方就是进出桶的时候判断下一个是否合法,所以范围应该是开区间,最多到倒数第二个,不然倒数第一个怎么判

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,a[50010],lmax[50010],lmin[50010],rmax[50010],rmin[50010],t[100010];
int search(int l,int r,int mid)
{
    int ans=0;
    lmax[mid]=lmin[mid]=a[mid];
    rmax[mid+1]=rmin[mid+1]=a[mid+1];
    for(int i=mid-1;i>=l;i--){
        lmax[i]=max(lmax[i+1],a[i]);
        lmin[i]=min(lmin[i+1],a[i]);
    }
    for(int i=mid+2;i<=r;i++){
        rmax[i]=max(rmax[i-1],a[i]);
        rmin[i]=min(rmin[i-1],a[i]);
    }
    for(int i=l;i<=mid;i++){
        int j=lmax[i]-lmin[i]+i;
        if(j<=r&&j>=mid+1&&lmin[i]<rmin[j]&&lmax[i]>rmax[j]) ans++;
    }
    for(int i=mid+1;i<=r;i++){
        int j=i-rmax[i]+rmin[i];
        if(j>=l&&j<=mid&&lmin[j]>rmin[i]&&lmax[j]<rmax[i]) ans++;
    }
    int L=mid+1,R=mid+1;
    while(R<=r&&rmin[R]>lmin[l]){
        t[rmax[R]-R+n]++;
        R++;
    }
    while(L<=r&&rmax[L]<lmax[l]){
        t[rmax[L]-L+n]--;
        L++;
    }
    for(int i=l;i<=mid;i++){
        while(R>mid+1&&rmin[R-1]<lmin[i]){
            R--;
            t[rmax[R]-R+n]--;
        }
        while(L>mid+1&&rmax[L-1]>lmax[i]){
            L--;
            t[rmax[L]-L+n]++;
        }
        if(t[lmin[i]-i+n]>0) ans+=t[lmin[i]-i+n];
    }
    for(int i=mid+1;i<=r;i++) t[rmax[i]-i+n]=0;
    L=mid,R=mid;
    while(L>=l&&lmin[L]>rmin[r]){
        t[lmax[L]+L]++;
        L--;
    }
    while(R>=l&&lmax[R]<rmax[r]){
        t[lmax[R]+R]--;
        R--;
    }
    for(int i=r;i>=mid+1;i--){
        while(L<mid&&lmin[L+1]<rmin[i]){
            L++;
            t[lmax[L]+L]--;
        }
        while(R<mid&&lmax[R+1]>rmax[i]){
            R++;
            t[lmax[R]+R]++;
        }
        if(t[rmin[i]+i]>0) ans+=t[rmin[i]+i];
    }
    for(int i=l;i<=mid;i++) t[i+lmax[i]]=0;
    return ans;
}
int work(int l,int r)
{
    if(l==r) return 1;
    int mid=(l+r)>>1;
    return work(l,mid)+work(mid+1,r)+search(l,r,mid);
}
int main()
{
    scanf("%d",&n);
    int u,v;
    for(int i=1;i<=n;i++){
        scanf("%d%d",&u,&v);
        a[u]=v;
    }
    printf("%d\n",work(1,n));
}
别颓

猜你喜欢

转载自www.cnblogs.com/jrf123/p/11199197.html
T3