zoukankan      html  css  js  c++  java
  • 【做题记录】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;
    }
    
  • 相关阅读:
    《那些年啊,那些事——一个程序员的奋斗史》——29
    《那些年啊,那些事——一个程序员的奋斗史》——28
    《那些年啊,那些事——一个程序员的奋斗史》——28
    《那些年啊,那些事——一个程序员的奋斗史》——29
    《那些年啊,那些事——一个程序员的奋斗史》——30
    《那些年啊,那些事——一个程序员的奋斗史》——29
    《那些年啊,那些事——一个程序员的奋斗史》——28
    小议如何改变指针的指向
    X Window Troubleshooting
    终于把傅雷的《世界美术名作二十讲》看完了
  • 原文地址:https://www.cnblogs.com/EricQian/p/15368919.html
Copyright © 2011-2022 走看看