1. 程式人生 > >BZOJ 2223: [Coci 2009]PATULJCI

BZOJ 2223: [Coci 2009]PATULJCI

看題面戳我

因為給了上限,言外之意演算法與資料的上限有關,猜測是陣列,但想一想不可能,所以應該是權值線段樹

綜合一下,猜測應該是區間第K大,也就是主席樹了

最暴力的做法:每次詢問排序後檢視是否有連續超過\frac{r-l+1}{2}的數相同

Q:為什麼要是超過\frac{r-l+1}{2}的數相同呢?為什麼不能在給一個數據呢?

A:這是區間的一半,排序後如果存在則相同的數必須為中位數,證明略

所以結論與之前的猜想一結合,就是求區間的中位數的個數是否超過\frac{r-l+1}{2}

這與權值線段樹上一個點的值就是下標的個數的思想完全匹配,所以就是一顆主席樹

#include<cstdio>
#include<iostream>
using namespace std;
 
const int N=4500000,M=3e5+5;;
int n,m,T,ans,rt[M];
 
struct A
{
    int lc[N],rc[N],c[N],cnt;
    void add(int &p,int l,int r,int x)
    {
        lc[++cnt]=lc[p],rc[cnt]=rc[p],c[cnt]=c[p]+1,p=cnt;
        if(l==r) return;
        int mid=l+r>>1;
        if(mid>=x) add(lc[p],l,mid,x);
            else add(rc[p],mid+1,r,x);
    }
     
    int kth(int p1,int p2,int l,int r,int k,int bz)
    {
        if(l==r) return c[p2]-c[p1]>bz?l:0;  //判斷是否>r-l+1>>1,bz表示標準 
        int mid=l+r>>1,xx=c[lc[p2]]-c[lc[p1]];
        if(xx>=k) return kth(lc[p1],lc[p2],l,mid,k,bz);
        return kth(rc[p1],rc[p2],mid+1,r,k-xx,bz);
    }
}tree;
 
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) 
	{
		int x; scanf("%d",&x);
		rt[i]=rt[i-1],tree.add(rt[i],1,m,x);
	}  //建樹 
    scanf("%d",&T);
    while(T--)
    {
        int l,r; scanf("%d%d",&l,&r);
        ans=tree.kth(rt[l-1],rt[r],1,m,r-l+2>>1,r-l+1>>1); //查詢中位數 
        if(!ans) puts("no"); else printf("yes %d\n",ans);
    }
    return 0;
}