SPOJ QTREE5
USACO填坑计划

WC2013 糖果公园

HJWJBSR posted @ 2015年9月08日 22:52 in 题解 , 666 阅读

早有耳闻的sxbk题

一直以为带修改树上莫队一定有着很高的技巧

于是去膜了一发题解

才发现这道题主要是在排序上面加特技

考虑将询问分块,然后与普通莫队不同的地方是我们将询问按照左端点的块编号、右端点的块编号、在第几次修改之后三个关键字排序。

这样在n^(2/3)个块区间里面的修改因为时间有序了所以是O(n)的。然后询问的话反正区间内的修改是n^(2/3)的,区间开头的询问也可以暴力O(n)搞。

所以时间的操作是O(n^(5/3))的,而其他树上莫队的部分也是O(n^(5/3))

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
#define N 100050
#define M 400050
#define ll long long
#define eps 1e-5
int c[N*2][2],fi[N],fa[N],sg[N][2],rt,n,m,Sign[M],t[M];
int li[N],cl[N],mo[N][3],qu[N][2],ss,st,p,cnt[N];
int now,nt[M];ll ans[N],Ans,tot[N];
struct Color
 {
 	ll wi[N],v[N],s[N];
 	inline void Insert(int x)
 	 {Ans+=wi[++s[x]]*v[x];}
 	inline void Delete(int x)
 	 {Ans-=wi[s[x]--]*v[x];}
 } Aha;
inline int Read()
 {
 	int x=0;char y;
 	do y=getchar(); while (y<'0'||y>'9');
 	do x=x*10+y-'0',y=getchar(); while (y>='0'&&y<='9');
 	return x;
 }
inline void Line(int x,int y)
 {
 	c[++ss][0]=y;c[ss][1]=fi[x];fi[x]=ss;
 	c[++ss][0]=x;c[ss][1]=fi[y];fi[y]=ss;
 }
void DFS(int x)
 {
 	nt[sg[x][0]=++st]=x;tot[x]=2;
 	for (int i=fi[x];i;i=c[i][1])
 	 if (c[i][0]!=fa[x])
 	  {
 	  	 if (nt[st]!=x) nt[++st]=x,tot[x]++;
 	   	 fa[c[i][0]]=x,DFS(c[i][0]);
 	  }
 	nt[sg[x][1]=++st]=x;
 }
inline bool cmp(int x,int y)
 {return Sign[qu[x][0]]<Sign[qu[y][0]]||
   (Sign[qu[x][0]]==Sign[qu[y][0]]&&(Sign[qu[x][1]]<
   	  Sign[qu[y][1]]||(Sign[qu[x][1]]==Sign[qu[y][1]]&&
   	    t[x]<t[y])));}
void Pretreat()
 {
 	DFS(1);
 	rt=pow(st,2.0/3.0)+eps;
 	for (int i=1;i<=st;i++) Sign[i]=(i-1)/rt;
 	for (int i=1;i<=p;i++)
 	 {
 	 	if (sg[qu[i][1]][0]<sg[qu[i][0]][0])
 	 	  swap(qu[i][1],qu[i][0]);
 	 	qu[i][0]=sg[qu[i][0]][sg[qu[i][0]][1]<sg[qu[i][1]][1]];
 	 	qu[li[i]=i][1]=sg[qu[i][1]][0];
 	 }
 	for (int i=1;i<=now;i++)
 	  mo[i][1]=cl[mo[i][0]],cl[mo[i][0]]=mo[i][2];
 	for (int i=now;i>=1;i--)
 	  cl[mo[i][0]]=mo[i][1];
 	sort(li+1,li+p+1,cmp);
 }
inline void Move(int x,int y)
 {
 	bool r=(cnt[mo[x][0]]<tot[mo[x][0]])
 	   &&(cnt[mo[x][0]]>0);
 	if (r) Aha.Delete(mo[x][y]);
 	cl[mo[x][0]]=mo[x][3-y];
 	if (r) Aha.Insert(mo[x][3-y]);
 }
inline void Modify(int x,int y)
 {
 	cnt[x]+=y;
 	if (!cnt[x]||cnt[x]==tot[x]) Aha.Delete(cl[x]); else
 	 if (cnt[x]-y==0||cnt[x]-y==tot[x]) Aha.Insert(cl[x]);
 }
void Solve()
 {
 	int le=1,ri=0;now=0;
 	for (int i=1;i<=p;i++)
 	 {
 	 	int k=qu[li[i]][0],l=qu[li[i]][1];
 	 	for (;le<k;le++) Modify(nt[le],-1);
 	 	for (;le>k;) Modify(nt[--le],1);
 	 	for (;ri>l;ri--) Modify(nt[ri],-1);
 	 	for (;ri<l;) Modify(nt[++ri],1);
 	 	for (;now<t[li[i]];) Move(++now,1);
 	 	for (;now>t[li[i]];) Move(now--,2);
 	 	ans[li[i]]=Ans;
 	 }
 	return;
 }
int main()
 {
 	n=Read();m=Read();p=Read();
 	for (int i=1;i<=m;i++) Aha.v[i]=Read();
 	for (int i=1;i<=n;i++) Aha.wi[i]=Read();
 	for (int i=1;i<n;i++)
 	  Line(Read(),Read());
 	for (int i=1;i<=n;i++) cl[i]=Read();
 	int o=p;p=0;
    while (o--)
 	 if (Read())
 	   qu[++p][0]=Read(),qu[p][1]=Read(),t[p]=now;else
 	   mo[++now][0]=Read(),mo[now][2]=Read();
 	Pretreat();
 	Solve();
 	for (int i=1;i<=p;i++)
 	  printf("%lld\n",ans[i]);
 	return 0;
 }

登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter