【NOIP提高】奇袭

来源:互联网 发布:2016网络作家富豪榜 女 编辑:程序博客网 时间:2024/05/16 04:55

Description

由于各种原因,桐人现在被困在Under World(以下简称UW)中,而UW马上要迎来最终的压力测试——魔界入侵。
唯一一个神一般存在的Administrator被消灭了,靠原本的整合骑士的力量是远远不够的。所以爱丽丝动员了UW全体人民,与整合骑士一起抗击魔族。
在UW的驻地可以隐约看见魔族军队的大本营。整合骑士们打算在魔族入侵前发动一次奇袭,袭击魔族大本营!
为了降低风险,爱丽丝找到了你,一名优秀斥候,希望你能在奇袭前对魔族大本营进行侦查,并计算出袭击的难度。
经过侦查,你绘制出了魔族大本营的地图,然后发现,魔族大本营是一个N×N的网格图,一共有N支军队驻扎在一些网格中(不会有两只军队驻扎在一起)。
在大本营中,每有一个k×k(1≤k≤N)的子网格图包含恰好k支军队,我们袭击的难度就会增加1点。
现在请你根据绘制出的地图,告诉爱丽丝这次的袭击行动难度有多大。

Solution

一开始还以为是什么树套树什么的,结果打了个暴力,就不管了。

转化为序列上的问题

因为每行每列都 只会出现一个点,b[x][y]有点,那么a[x]=y。
一个k*k的正方形里面有一个点,就相当于一个长度为k的区间中最大值-最小值=k-1就合法。

怎么做——分治

线段树没有想出来n log n的方法。
想一想经典的方法,ans[l,r]=ans[l,mid-1]+ans[mid+1,r]+跨区间的答案。
问题就是跨区间的答案要怎么算。

跨区间怎么算

因为要跨区间,那么符合答案的一段区间的[l1,r1],l1∈[l,mid],r1∈[mid+1,r]
有四种情况:
1、最小值在左边,最大值在左边。
2、最小值在右边,最大值在右边。
3。最小值在左边,最大值在右边。
4、最小值在右边,最大值在左边。
这里只讨论情况1和情况3,因为情况2和4与1和3对称。
先处理两个数组,da[i],xiao[i]。因为要跨区间,所以如果区间的左端点是i∈[l,mid],那么最大值值在[i,mid]区间内的,所以da[i]在[l,mid]的值是ai...mid的最大值;如果要在[mid+1,r]区间,那么da[i]的值是amid+1...r的最大值。
xiao[i]同理。
情况1:最小值在左边,最大值在左边。
枚举一个左端点j,假设有一个右端点i,因为最值在左边,那么满足i-j=da[j]-xiao[j],所以j可以算出来i=j+(da[j]-xiao[j]),那么只要满足左边的最小值<右边的最小值,左边的最大值>右边的最大值就好了(即da[j]>=da[i]&&xiao[j]<=xiao[i)。
情况3:最小值在左边,最大值在右边。
枚举一个左端点i,如果有一个右端点j,因为最小值在左边,最大值在右边,所以满足左边的最小值<右边的最小值,左边的最大值<右边的最大值。那么左端点固定,右端点就会有一段可行范围。
可行区间还要满足j-i=da[j]-xiao[i],转化一下变成j-da[j]=i-xiao[i],所以把所有在可行区间的j,用一个桶tong[j-da[j]]++,那么每次答案加上tong[i-xiao[i]]就好了,i-da[j]可能会变成负数,所以需要加上mid变成tong[i-xiao[i]+mid]。
枚举可行区间不用从头枚举如果i为左端点的可行区间是l[i],r[i],那么l[i+1]>=l[i],r[i+1]>=r[i],因为da[i]和xiao[i]满足单调性。

Code

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<cmath>#define fo(i,a,b) for(i=a;i<=b;i++)#define fod(i,a,b) for(i=a;i>=b;i--)using namespace std;const int maxn=500007;int i,j,k,l,t,n,m;long long ans;int a[maxn],da[maxn],xiao[maxn],tong[maxn];void solve(int l,int r){    if(l>r)return;    int mid=(l+r)/2,i,j,k,t;    da[mid]=xiao[mid]=a[mid];    da[mid+1]=xiao[mid+1]=a[mid+1];    fod(i,mid-1,l)da[i]=max(da[i+1],a[i]),xiao[i]=min(xiao[i+1],a[i]);    fo(i,mid+2,r)da[i]=max(da[i-1],a[i]),xiao[i]=min(xiao[i-1],a[i]);    fod(j,mid,l){        i=j+(da[j]-xiao[j]);        if(i>=mid&&i<=r&&da[j]>=da[i]&&xiao[j]<=xiao[i])ans++;    }    fo(i,mid+1,r){        j=i-(da[i]-xiao[i]);        if(j<=mid&&j>=l&&da[j]<=da[i]&&xiao[j]>=xiao[i])ans++;    }    k=t=mid+1;    fod(i,mid,l){        while(t<=r&&xiao[t]>=xiao[i])tong[da[t]-t+mid]++,t++;        while(k<t&&da[k]<=da[i])tong[da[k]-k+mid]--,k++;        ans+=tong[xiao[i]-i+mid];    }    fo(i,k,t-1)tong[da[i]-i+mid]--;    k=t=mid;    fo(i,mid+1,r){        while(k>=l&&xiao[k]>=xiao[i])tong[da[k]+k-mid]++,k--;        while(t>k&&da[t]<=da[i])tong[da[t]+t-mid]--,t--;        ans+=tong[xiao[i]+i-mid];    }    fo(i,k+1,t)tong[da[i]+i-mid]--;    solve(l,mid-1),solve(mid+1,r);}int main(){//  freopen("fan.in","r",stdin);    scanf("%d",&n);    fo(i,1,n){        scanf("%d%d",&k,&l);        a[k]=l;    }    solve(1,n);    printf("%I64d\n",ans);}
1 0
原创粉丝点击