歌名 - 歌手
0:00

    【NOIP2016提高A组8.12】奇袭

    题目

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

    分析

    想到,可以把题目简化为在一段数中,选择一段区间,使的该区间的数连续,求方案数。
    接着可以进一步转换为在一段数中,选择相邻k个数,使得这k个数中的最大值减去最小值等于k-1,求方案数。
    然后考虑如何解决这个问题。
    我们用分治的思想。
    对于一段区间,左边界为l,右边界为r
    这里写图片描述
    那么这一段区间的答案\(ans(l,r)=ans(l,mid)+ans(mid+1,r)+这k个数穿过mid的方案数\)
    首先知道一个合法的区间\([i,j]\)\(j-i=区间[i,j]中的最大值-区间[i,j]中的最小值\)
    这里分两种情况:

    最大最小值都在同一侧

    现在假设都在左侧的情况,即在区间\([l,mid]\)中。右侧的情况自己思考。
    先定义:

    mal[x]:表示区间[x,mid]中的最大值(x在区间[l,mid]中)
    mil[x]:表示区间[x,mid]中的最小值(x在区间[l,mid]中)
    mar[x]:表示区间[mid+1,x]中的最大值(x在区间[mid+1,r]中)
    mir[x]:表示区间[mid+1,x]中的最小值(x在区间[mid+1,r]中)
    

    我们枚举一个i,i从mid向l移动。设j为区间的右边界
    因为最大最小值都在左侧,
    根据合法区间的定义,
    可以轻松求出j,\(j=i+mal[i]-mil[i]\)
    这里写图片描述
    但是j不一定是合法的,
    1、j必须在区间\([mid+1,r]\)之间
    2、mar[j]必须小于mal[i],mir[j]必须大于mil[i],否则最大最小值就不都在左侧了。
    右侧的情况类似。

    最大最小值在异侧

    现在假设最大值在右侧,即在区间\([mid+1,r]\)中;现在假设最小值在右侧,即在区间\([l,mid]\)中。
    我们同样枚举一个i,i从mid向l移动。设j为区间的右边界
    这里写图片描述
    我们再定义两个指针z和z1从mid+1向r移动。
    因为最大值在右侧,所以mal[i]应该小于mar[z],那么当mal[i]>mar[z]时,将指针z向右移;因为mal和mar都是单调的,对于当前的i,因为区间mar[z-1]一定小于mal[i],都是不合法的。
    又因为最小值在左侧,所以mil[i]应该小于mir[z1],那么当mir[z1]>mil[i]时,将指针z1向右移;因为mil和mir也都是单调的,对于当前的i,因为区间mir[z1-1]一定大于mil[i],都是合法的。
    于是区间[z,z1]中的数都有可能是合法的j。
    再根据合法区间的定义,移项得到\(mil[i]-i=mar[j]-j\)
    定义一个桶t[],
    那么当z移动时,经过的点都是不合法的,就将t[mar[z]-z]减去一;
    那么当z1移动时,经过的点都是合法的,就将t[mar[z1]-z1]加上一。
    这里写图片描述
    最后,将ans加上t[mil[i]-i]。
    桶记住要清零。
    另一种情况自己考虑。

    #include <cmath>
    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <algorithm>
    #include <queue>
    const int maxlongint=2147483647;
    const int mo=1000000007;
    const int N=50005;
    using namespace std;
    int a[N*2],n,mal[N*2],mar[N*2],mil[N*2],mir[N*2],t[N*5],j,z,z1;
    int solve(int l,int r)
    {
    	int mid=(l+r)/2,ans=0;
    	if(l!=r) ans=solve(l,mid)+solve(mid+1,r);
    		else return 1;
    	for(int i=l;i<=r;i++) mar[i]=mal[i]=0,mir[i]=mil[i]=maxlongint;
    	for(int i=mid;i>=l;i--)
    	{
    		mil[i]=min(mil[i+1],a[i]);
    		mal[i]=max(mal[i+1],a[i]);
    	}
    	for(int i=mid+1;i<=r;i++)
    	{
    		mir[i]=min(mir[i-1],a[i]);
    		mar[i]=max(mar[i-1],a[i]);
    	}
    	//极值都在左边
    	for(int i=mid;i>=l;i--)
    	{
    		j=i+mal[i]-mil[i];
    		if((j<=mid) || (j>r)) continue;
    		if(mal[i]>mar[j] && mil[i]<mir[j]) ans++;
    	}
    	//极值都在右边
    	for(int i=mid+1;i<=r;i++)
    	{
    		j=i-mar[i]+mir[i];
    		if((j>=mid+1) || (j<l)) continue;
    		if(mar[i]>mal[j] && mir[i]<mil[j]) ans++;
    	}
    	//min在左,max在右
    	z=z1=mid+1;
    	for(int i=mid;i>=l;i--)
    	{
    		while(z<=r && mar[z]<mal[i]) 
    		{
    			t[mar[z]-z+N]--;
    			z++;
    		}
    		while(z1<=r && mir[z1]>mil[i])
    		{
    			t[mar[z1]-z1+N]++;
    			z1++;
    		}
    		if(t[mil[i]-i+N]>=0)
    			ans+=t[mil[i]-i+N];
    	}
    	for(int i=mid+1;i<=r;i++) t[mar[i]-i+N]=0;
    	//max在左,min在右
    	z=z1=mid+1;
    	for(int i=mid;i>=l;i--)
    	{
    		while(z1<=r && mir[z1]>mil[i])
    		{
    			t[mir[z1]+z1+N]--;
    			z1++;
    		}
    		while(z<=r && mal[i]>mar[z])
    		{
    			t[mir[z]+z+N]++;
    			z++;
    		}
    		if(t[mal[i]+i+N]>=0)
    			ans+=t[mal[i]+i+N];
    	}
    	for(int i=mid+1;i<=r;i++) t[mir[i]+i+N]=0;
    	return ans;
    }
    int main()
    {
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++)
    	{
    		int x,y;
    		scanf("%d%d",&x,&y);
    		a[x]=y;
    	}
    	printf("%d",solve(1,n));
    }
    
    posted @ 2018-05-15 22:38  无尽的蓝黄  阅读(195)  评论(0编辑  收藏  举报