【做题记录】max-min+1=len 区间计数

(来源:XJ高质量原创题)

原题地址

弱化版:CF526F Pudding Monsters

弱化版

题意:(n imes n) 的棋盘上有 (n) 颗棋子,每行每列都有且仅有一颗棋子,求出有多少个 (k imes k) 的子棋盘也满足每行每列只有一颗棋子。

将棋盘的 (x) 轴看作上一题中每个点的下标,(y) 轴看作这个点的权值。

其实就是树退化为了链,而 (m=1) 的情况。

题意

给定一棵树,每一个点有一个权值 (a_i)({a}) 构成一个排列。

询问有多少个点集 (S={u_1,u_2,dots,u_k}(1le jle n)) 满足这些点构成联通块不超过 (m) 个,且 (max_{1le ile k}{a_{u_{i}}}-min_{1le ile k}{a_{u_{i}}}+1=k)

(nle 10^5,mle 7)

题解

考虑枚举值域的右端点 (r),考虑加入这个右端点时,对与 ([1,r-1]) 中的最短点的联通块个数的变化。

先假设假设这个点与其他无边相连,那么联通块数加 (1)

对于每一条与点集中的点相连的边 ((num(r),ver)) ,都会使左端点在 ([1,val(ver)]) 中的联通块数量 (-1)(num(x)) 表示权值为 (x) 的点的编号)。

之后我们就将题意简化为:

  • 区间加上一个数

  • 求出整个数列中 (le m) 的数有多少个

  • 保证 (a[1]=1)

考虑到 (m) 比较小,猜测答案复杂度为 (O(nmlog n))

我们将加减操作同普通的线段树操作,主要改变 ( ext{pushup}) 操作。

在线段树的每一个节点 ([l,r]) 上维护:

  • (minn) : 表示在 ([l,r]) 区间上数的最小值。

  • (cnt[7]) : 表示这段区间内值域在 ([minn,minn+6]) 之间的数分别有多少。

之后根据两个子区间的最小值就可以愉快地转移啦!!

// Author:A weak man named EricQian
// expect : 100 pts
#include<bits/stdc++.h>
using namespace std;
#define infll 0x7f7f7f7f7f7f7f7f
#define inf 0x3f3f3f3f
#define Maxn 500005
#define Maxm 7
typedef long long ll;
inline int rd()
{
	 int x=0;
     char ch,t=0;
     while(!isdigit(ch = getchar())) t|=ch=='-';
     while(isdigit(ch)) x=x*10+(ch^48),ch=getchar();
     return x=t?-x:x;
}
int n,m,tot;
int val[Maxn],num[Maxn];
int hea[Maxn],nex[Maxn<<1],ver[Maxn<<1];
struct TREE { ll minn,laz,cnt[Maxm]; }tree[Maxn<<2];
ll ans;
inline void add(int x,int y){ ver[++tot]=y,nex[tot]=hea[x],hea[x]=tot; }
inline void pushup(int p)
{
	 int x=p<<1,y=p<<1|1,tmp;
	 if(tree[x].minn>tree[y].minn) swap(x,y);
	 tree[p].minn=tree[x].minn;
	 for(int i=0;i<m;i++) tree[p].cnt[i]=tree[x].cnt[i];
	 tmp=tree[y].minn-tree[x].minn;
	 for(int i=tmp;i<m;i++) tree[p].cnt[i]+=tree[y].cnt[i-tmp];
}
inline void pushdown(int p)
{
	 tree[p<<1].laz+=tree[p].laz,tree[p<<1].minn+=tree[p].laz;
	 tree[p<<1|1].laz+=tree[p].laz,tree[p<<1|1].minn+=tree[p].laz;
	 tree[p].laz=0;
}
void build(int p,int nl,int nr)
{
	 if(nl==nr) { tree[p].minn=tree[p].cnt[0]=1; return; }
	 int mid=(nl+nr)>>1;
	 build(p<<1,nl,mid),build(p<<1|1,mid+1,nr);
	 pushup(p);
}
void add(int p,int nl,int nr,int l,int r,int k)
{
	 if(nl>=l && nr<=r) { tree[p].minn+=k,tree[p].laz+=k; return; }
	 pushdown(p);
	 int mid=(nl+nr)>>1;
	 if(mid>=l) add(p<<1,nl,mid,l,r,k);
	 if(mid<r) add(p<<1|1,mid+1,nr,l,r,k);
	 pushup(p);
}
int main()
{
	 n=rd(),m=rd();
	 for(int i=1;i<=n;i++) val[i]=rd(),num[val[i]]=i;
	 for(int i=1,x,y;i<n;i++) x=rd(),y=rd(),add(x,y),add(y,x);
	 build(1,1,n);
	 for(int r=1;r<=n;r++)
	 {
	 	 if(r!=1) add(1,1,n,1,r-1,1);
	 	 for(int i=hea[num[r]];i;i=nex[i])
	 	 	 if(val[ver[i]]<val[num[r]])
	 	 	 	 add(1,1,n,1,val[ver[i]],-1);
	 	 for(int i=0;i<m;i++) ans+=tree[1].cnt[i];
	 	 ans-=(n-r);
	 }
	 printf("%lld
",ans);
	 return 0;
}
原文地址:https://www.cnblogs.com/EricQian/p/15368919.html