整体二分

        很好的一篇入门:

                   http://www.cnblogs.com/zig-zag/archive/2013/04/18/3027707.html

        跟时间分治一样,我们需要把握好一个序。

        它的适用范围一般是可以二分的题目,例如区间k大之类恶心的东西

        你说带修改k大?树上带修改k大?主席树上啊……孩子醒醒吧,树套树你会崩溃的尤其还不是一般的线段树

        Zig-Zag神牛那篇讲的是带修改的区间k大,我就无聊说说跟它不完全一样的树上k大(虽然其实差不多)

        好吧还是差很多的,蒟蒻只能yy出一个很不靠谱的方法……

        首先,入口是solve(l,r,L,R)表示待处理区间为Seq[l]~Seq[r],答案范围为L,R

        类似区间k大,我们在小于MID的点+1,求u到v路径上的和,便是当前有多少数小于mid

        至于怎么搞……树链剖分或者LCT都行了(大家散吧博主傻逼我去写树套树了~喂别跑了LCT还是比树套树好写的!!!!!)

        然后抓住一个序的问题,将小于MID的修改,以及cur没有到所需k的查询,加入QL队列,否则加入QR队列。
        切记维护一个序序序序!!!!

        你说代码,没有!!!

        update:无聊还是写了下,顺便练一下代码能力,暂时没过,不过问题不大

#include <cstdio>
#include <vector>
#include <algorithm>

using namespace std;

const int MAXN = 1001;
const int MAXM = 1001; 

struct Node *null;
struct Node
{
	int v,sum;
	int size;
	Node *ch[2],*f;
	void maintain(){size = 1+ch[0]->size+ch[1]->size, sum = v+ch[0]->sum+ch[1]->sum;}
	bool check(){return f != null && (f->ch[0] == this || f->ch[1] == this);}
	int d(){return f->ch[1] == this ? 1 : 0;}
	void setc(Node *c,int d){ch[d] = c, c->f = this;}
	Node (){ch[0] = ch[1] = f = null, size = 1, v = sum = 0;}
	Node (int x){ch[0] = ch[1] = f = NULL, size = 0, v = sum = 0;};
};

struct Q
{
	int oper,u,v,k,id,cur;
};

int n,m,cnt;
int id[MAXN],idx[MAXN],ans[MAXN];
int temp[MAXM];
Q Seq[MAXM],QL[MAXM],QR[MAXM];
Node *p[MAXN];
vector <int> G[MAXN];

inline void READ(int &x)
{
	char c;
	x = 0;
	do c = getchar(); while (c < '0' || c > '9');
	do x = x*10+c-48, c = getchar(); while (c >= '0' && c <= '9');
}

bool cmp(int x,int y)
{
	return id[x] < id[y];
}

void rotate(Node *p)
{
	Node *x = p->f;
	int d = p->d();
	if (!x->check()) p->f = x->f;
	else x->f->setc(p,x->d());
	x->setc(p->ch[d^1],d);
	p->setc(x,d^1);
	x->maintain();
}

void Splay(Node *p)
{
	while (p->check())
	{
		Node *x = p->f;
		if (!x->check()) rotate(p);
		else p->d() == x->d() ? (rotate(x),rotate(p)) : (rotate(p),rotate(p));
	}
	p->maintain();
}

void Access(Node *p)
{
	for (Node *q = null; p != null; p = p->f)
	{
		Splay(p);
		p->setc(q,1);
		(q = p)->maintain();
	}
}

inline int query(Node *u,Node *v)
{
	Access(u);
	int ans = 0;
	for (Node *q = null; v != null; v = v->f)
	{
		Splay(v);
		if (v->f == null) ans = q->sum+v->sum;
		v->setc(q,1);
		(q = v)->maintain();
	}
	return ans;
}

inline void modify(Node *u,int delta)
{
	u->v += delta;
	u->sum += delta;
}

void dfs(int u,int fa)
{
	p[u]->f = p[fa];
	for (int i=0;i<G[u].size();i++) if (G[u][i] != fa) dfs(G[u][i],u);
}

void solve(int l,int r,int L,int R)
{
	if (L == R) 
	{
		for (int i=l;i<=r;i++) if (Seq[i].oper == 1) ans[Seq[i].id] = L;
		return;
	}
	int MID = (L+R)/2;
	for (int i=l;i<=r;i++)
	{
		if (Seq[i].oper == 1) temp[i] = query(p[Seq[i].u],p[Seq[i].v]);
		else if (Seq[i].v <= MID) modify(p[Seq[i].u],1);
	}
	for (int i=l;i<=r;i++) if (Seq[i].oper == 2 && Seq[i].v <= MID) modify(p[Seq[i].u],-1);
	int LenL = 0, LenR = 0;
	for (int i=l;i<=r;i++)
	{
		if (Seq[i].oper == 1)
		{
			if (temp[i]+Seq[i].cur >= Seq[i].k) QL[++LenL] = Seq[i];
			else
			{
				Seq[i].cur += temp[i];
				QR[++LenR] = Seq[i];
			}
		}
		else 
		{
			if (Seq[i].v <= MID) QL[++LenL] = Seq[i];
			else QR[++LenR] = Seq[i];
		}
	}
	cnt = l-1;
	for (int i=1;i<=LenL;i++) Seq[++cnt] = QL[i];
	for (int i=1;i<=LenR;i++) Seq[++cnt] = QR[i];
	solve(l,l+LenL-1,L,MID);
	solve(l+LenL,r,MID+1,R); 
}

int main()
{
	null = new Node(1);
	READ(n);READ(m);
	for (int i=1;i<=n;i++) READ(id[i]),idx[i] = i;
	sort(idx+1,idx+n+1,cmp);
	for (int i=1;i<=n;i++) Seq[++cnt] = (Q){2,idx[i],i,0,0,0};
	for (int i=1;i<=n;i++) p[i] = new Node();
	p[0] = null;
	for (int i=1;i<n;i++)
	{
		int u,v;
		READ(u);READ(v);
		G[u].push_back(v);
		G[v].push_back(u);
	}
	dfs(1,0);
	for (int i=1;i<=m;i++)
	{
		int l,r,k;
		scanf("%d%d%d",&l,&r,&k);
		Seq[++cnt] = (Q){1,l,r,k,i,0};
	}
    solve(1,cnt,1,n);
	for (int i=1;i<=m;i++) printf("%d\n",id[idx[ans[i]]]);
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值