$$AVICII$$

模板(ac)

谢天谢地!

首先鸣谢人帅话骚的好心人lyd的细心指导,lnc提壶灌顶的思维引导,耗时1.5天,我。。终于调过了

好,步入正题:

30%

  暴搜,不解释

#include<bits/stdc++.h>
#define int long long
#define MAXN 100010
using namespace std;
inline int read(){
	int s=0,w=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar(); }
	while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
	return s*w;
}
#define kd read()
map<int ,bool >mp[MAXN];
struct rr{
	int nt,to;
}bl[MAXN<<1];int hd[MAXN],itot;
void add(int x,int y){
	bl[++itot].to=y;
	bl[itot].nt=hd[x];
	hd[x]=itot;
}
int n,m,Q;
int lis[MAXN];
int qx[MAXN],qc[MAXN];
int fat[MAXN],zui[MAXN],lim[MAXN],sm[MAXN];
void dfs(int u,int fa){
	fat[u]=fa;
	for(int i=hd[u];i;i=bl[i].nt){
		if(bl[i].to==fa)continue;
		dfs(bl[i].to,u);
	}
}
void work(int pos,int c){
	for(int i=pos;i;i=fat[i]){
		++sm[i];
		if(sm[i]>lim[i]) continue;
		if(!mp[i][c]) ++zui[i];
		mp[i][c]=1;
	}
}
signed main(){
//	freopen("da.in","r",stdin);
	n=kd;
	for(int i=1,a,b;i<n;++i){
		a=kd;b=kd;
		add(a,b);add(b,a);
	}
	for(int i=1;i<=n;++i)
		lim[i]=kd;
	dfs(1,0);	
	m=kd;
	for(int i=1;i<=m;++i){
		qx[i]=kd;qc[i]=kd;
		lis[i]=qc[i];	
	}
	sort(lis+1,lis+m+1);
	int lcnt=unique(lis+1,lis+m+1)-(lis+1);
	for(int i=1;i<=m;++i){
		qc[i]=lower_bound(lis+1,lis+lcnt+1,qc[i])-lis;
		work(qx[i],qc[i]);
	}
	Q=kd;
	int x;
	while(Q){
		--Q;
		x=kd;
		printf("%lld\n",zui[x]);
	}
}

 70%

  出题人是真的好心,造了40%的无脑差分的数据(和雨天的尾巴一样)。

#include<cstdio>
#include<iostream>
#include<map>
#define go(i) for(int i=head[x];i;i=nxt[i])if(to[i]!=fa[x])
#define lch ch[now][0]
#define rch ch[now][1]
#define N 100005
using namespace std;

int n,m,num_bian,Q,num_map,tot,Mi=0x3f3f3f3f;
int head[N],nxt[N<<1],to[N<<1],t[N],fa[N],buc[1005][1005];
int ch[N*50][2],sum[N*50],ret[N],rt[N];//树上差分,空间复杂度O(mlogm),每次多一个链为log,有m次修改是mlogm
map<int,int>turn;

inline void add(int x,int y){
	to[++num_bian]=y;nxt[num_bian]=head[x];head[x]=num_bian;
}
inline void Dfs(int x){go(i) fa[to[i]]=x,Dfs(to[i]);}
inline void change(int &now,int l,int r,int x){
	if(!now) now=++tot;
	if(l==r)return (void)(sum[now]=1);
	int mid=(l+r)>>1;
	if(x<=mid)change(lch,l,mid,x);
	else change(rch,mid+1,r,x);
	sum[now]=sum[lch]+sum[rch];
}
inline int merge(int x,int y,int l,int r){
	if(!x||!y) return x+y;
	if(l==r) return x;
	int mid=(l+r)>>1;
	ch[x][0]=merge(ch[x][0],ch[y][0],l,mid);
	ch[x][1]=merge(ch[x][1],ch[y][1],mid+1,r);
	sum[x]=sum[ch[x][0]]+sum[ch[x][1]];
	/*printf("l:%d r:%d sum:%d\n",l,r,sum[x]);
	printf("sum[rt[3]]=%d\n",sum[rt[3]]);*/
	return x;
}
inline void Dfs1(int x){go(i) Dfs1(to[i]),rt[x]=merge(rt[x],rt[to[i]],1,m);ret[x]=sum[rt[x]];}
int main(){
	scanf("%d",&n);
	for(int i=1,x,y;i<=n-1;++i) scanf("%d%d",&x,&y),add(x,y),add(y,x);
	for(int i=1;i<=n;++i)scanf("%d",&t[i]),Mi=min(Mi,t[i]);Dfs(1);
	if(Mi!=1e5){
		scanf("%d",&m);
		for(int i=1,x,col;i<=m;++i)
		{scanf("%d%d",&x,&col);while(1){if(!x)break;if(t[x]>0){t[x]--;if(!buc[x][col]) buc[x][col]=1,buc[x][0]++;}x=fa[x];}}
		scanf("%d",&Q);for(int i=1,x;i<=Q;++i)scanf("%d",&x),printf("%d\n",buc[x][0]);return 0;
	}
	else{
		scanf("%d",&m);
		for(int i=1,pos,color;i<=m;++i){
			scanf("%d%d",&pos,&color);
			(turn.find(color)==turn.end()?turn[color]=++num_map:0);
			color=turn[color];change(rt[pos],1,m,color);
		}
		Dfs1(1);scanf("%d",&Q);
		for(int i=1,x;i<=Q;++i)scanf("%d",&x),printf("%d\n",ret[x]);return 0;
	}
}

 100%

  一个很厉害的思路,我一直局限在以球的种类为下标建树,但其实忽略了一个很有用的性质:一个节点在一个时刻最多只有一个球。也就是启发我们以时间为下标,只考虑这一时刻是否有球,或是否球有贡献。这样可以能兼顾到70%的忽略时间影响的问题。

  1.来自lnc的只建一棵树作为一个全局变量,运用启发式合并,考虑重儿子(操作数最多的节点)的树不清继承给父亲最优,开vector存每个节点的操作.

对于每一个节点,用一个线段树,下标为时间,存储这个时间
子树中是否加入了小球,这个小球对答案的贡献(如果之前有
这种颜色的小球贡献就是 0 )。
在线段树上二分就能求出这个节点的答案。
维护这棵线段树可以用启发式合并的方法。
用启发式合并的方式处理出当前子树中的所有操作,同时构建
出线段树。
#include<bits/stdc++.h>
#define MAXN 100100
#define TR (MAXN<<2)
using namespace std;
int n,m,Q;
map<int ,int >lis;int lcnt;
vector<int >tt[MAXN],cc[MAXN];
int sz[MAXN],son[MAXN],zui[MAXN],lim[MAXN];
int sm[TR],knd[TR],lb[TR];
#define ls (u<<1)
#define rs (u<<1|1)
int mp[MAXN];
struct rr{
	int nt,to;
}bl[MAXN<<1];int hd[MAXN],itot;
void add(int x,int y){
	bl[++itot].to=y;
	bl[itot].nt=hd[x];
	hd[x]=itot;
}
void dfs(int u,int fa){
	sz[u]+=tt[u].size();
	for(int i=hd[u];i;i=bl[i].nt){
		if(bl[i].to==fa)continue;
		dfs(bl[i].to,u);
		sz[u]+=sz[bl[i].to];
		if(sz[son[u]]<sz[bl[i].to])
			son[u]=bl[i].to;
	}
}
void down(int u){
	if(!lb[u])return ;
	sm[ls]=sm[rs]=0;
	knd[ls]=knd[rs]=0;
	lb[ls]=lb[rs]=1;
	lb[u]=0;
}
void up(int u){
	sm[u]=sm[ls]+sm[rs];
	knd[u]=knd[ls]+knd[rs];
}
void upd(int u,int l,int r,int pos,int v1,int v2){
	down(u);
	if(l==r){
		sm[u]+=v1;
		knd[u]+=v2;
		return ;
	}
	int mid=l+r>>1;
	if(pos<=mid)upd(ls,l,mid,pos,v1,v2);
	else upd(rs,mid+1,r,pos,v1,v2);
	up(u);
}
int query(int u,int l,int r,int xa){
	down(u);
	if(xa<=0)return 0;
	if(l==r)return knd[u];
	int mid=l+r>>1;
	if(xa>=sm[ls]){
		int lsm=knd[ls];
		lsm+=query(rs,mid+1,r,xa-sm[ls]);
		return lsm;
	}
	return query(ls,l,mid,xa);
}
void insert(int u){
	int tim,co;
	for(int k=0;k<tt[u].size();++k){
		tim=tt[u][k];co=cc[u][k];
		if(!mp[co])upd(1,1,m,(mp[co]=tim),1,1);
		else if(mp[co]>tim){
			upd(1,1,m,mp[co],0,-1);
			upd(1,1,m,(mp[co]=tim),1,1);
		}
		else upd(1,1,m,tim,1,0);
	}	
}
void zy(int x,int y){
	for(int k=0;k<tt[y].size();++k){
		tt[x].push_back(tt[y][k]);
		cc[x].push_back(cc[y][k]);
	}
	tt[y].clear();cc[y].clear();
}
void shan(int u){
	sm[1]=knd[1]=0;
	lb[1]=1;
	down(1);
	for(int k=0;k<cc[u].size();++k)
		mp[cc[u][k]]=0;
}
void sou(int u,int fa){
	//cout<<u<<" "<<fa<<endl;
	for(int i=hd[u];i;i=bl[i].nt){
		if(bl[i].to==fa)continue;
		if(bl[i].to==son[u])continue;
		sou(bl[i].to,u);
		shan(bl[i].to);
	}
	if(son[u])sou(son[u],u);
	insert(u);
	for(int i=hd[u];i;i=bl[i].nt){
		if(bl[i].to==fa)continue;
		if(bl[i].to==son[u])continue;
		insert(bl[i].to);
	}
	zui[u]=query(1,1,m,lim[u]);
	if(son[u]){
		zy(son[u],u);
		swap(tt[son[u]],tt[u]);
		swap(cc[son[u]],cc[u]);
		for(int i=hd[u];i;i=bl[i].nt){
			if(bl[i].to==fa)continue;
			if(bl[i].to==son[u])continue;
			zy(u,bl[i].to);
		}
	}
}
int main(){
	//freopen("da.in","r",stdin);
	scanf("%d",&n);
	for(int i=1,a,b;i<n;++i){
		scanf("%d%d",&a,&b);
		add(a,b);add(b,a);
	}
	for(int i=1;i<=n;++i)
		scanf("%d",&lim[i]);
	scanf("%d",&m);
	for(int i=1,x,c;i<=m;++i){
		scanf("%d%d",&x,&c);
		if(!lis[c])lis[c]=++lcnt;
		tt[x].push_back(i);
		cc[x].push_back(lis[c]);
	}
	sz[0]=-1;
	dfs(1,0);
	//for(int i=1;i<=n;++i)
	//	printf("sz[%d]=%d\n",i,sz[i]);
	sou(1,0);
	scanf("%d",&Q);
	int x;
	while(Q){
		--Q;
		scanf("%d",&x);
		printf("%d\n",zui[x]);
	}
}

  O(n*(logn^2))插入为logn,询问logn,操作数n

  2.仍然可以动态开点,尽管最多建叶子节点数棵树,但动态开点还是有空间保证的,然而没调出来就弃了,现%whs和lockey

然而whs的码风太丑,还是粘lockey的吧

#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
#include<map>
using namespace std;
int n,m,Q,num,cet,root;
const int maxn=1e5+100;
int ls[410000],rs[410000],f[410000],qiunum[410000],colornum[410000],w[maxn],size[maxn],ma[maxn],is_here[maxn],loc[maxn],wolan[maxn],key[maxn];
vector<int>son[110000],point[110000];
int tmp[maxn],ans[maxn];
inline void down(int t){
	qiunum[ls[t]]=colornum[ls[t]]=0;
	qiunum[rs[t]]=colornum[rs[t]]=0;
	f[ls[t]]=f[rs[t]]=1;
	if(ls[ls[t]]==0&&rs[ls[t]]==0) f[ls[t]]=0;
	if(ls[rs[t]]==0&&rs[rs[t]]==0) f[rs[t]]=0;
	f[t]=0;
}
inline void build(int  &t,int l,int r){
	if(!t) t=++cet;
 	if(l==r) return ;
	int mid=(l+r)/2;
	build(ls[t],l,mid);
	build(rs[t],mid+1,r);
}
inline void dfs1(int x,int pre){
	size[x]=1+point[x].size();
	int mxn=0;
	for(register int i=0;i<son[x].size();i++){
		int y=son[x][i];
		if(y==pre) continue;
		dfs1(y,x);
		size[x]+=size[y];
		if(size[y]>mxn) ma[x]=y,mxn=size[y];
	}
}
inline void change(int t,int l,int r,int x){
	if(l==r){
		colornum[t]=0;
		return;
	}
	if(f[t]) down(t);
	int mid=(l+r)/2;
	if(mid>=x) change(ls[t],l,mid,x);
	else change(rs[t],mid+1,r,x);
	qiunum[t]=qiunum[ls[t]]+qiunum[rs[t]];
	colornum[t]=colornum[ls[t]]+colornum[rs[t]];
}
int landenum;
inline void add(int t,int l,int r,int x,int k){
	if(l==r){
		qiunum[t]+=k;
		colornum[t]+=k;
		if(wolan[w[l]]!=landenum) wolan[w[l]]=0,is_here[w[l]]=0,loc[w[l]]=0;
		if(k==1&&!is_here[w[l]]) is_here[w[l]]=1,loc[w[l]]=l,wolan[w[l]]=landenum;
		else if(k==1&&is_here[w[l]]){
			if(loc[w[l]]<l)  colornum[t]=0;
			else{
				change(1,1,m,loc[w[l]]);
				loc[w[l]]=l;
			}
		}
		return;
	}
	if(f[t]) down(t);
	int mid=(l+r)/2;
	if(mid>=x) add(ls[t],l,mid,x,k);
	else add(rs[t],mid+1,r,x,k);
	qiunum[t]=qiunum[ls[t]]+qiunum[rs[t]];
	colornum[t]=colornum[ls[t]]+colornum[rs[t]];
}
inline void go_add(int x,int pre,int k){
	for(register int i=0;i<point[x].size();i++)
		add(1,1,m,point[x][i],k);
	for(register int i=0;i<son[x].size();i++)
		 if(son[x][i]!=pre)
		 	 go_add(son[x][i],x,k);
}
inline int ask(int t,int x){
	if(x==0) return 0;
	if(qiunum[t]==0) return 0;
	if(t==0) return 0;
	if(qiunum[t]<x) return colornum[t];
	if(qiunum[t]==x) return colornum[t];
	if(qiunum[ls[t]]==x) return colornum[ls[t]];
	else if(qiunum[ls[t]]>x) return ask(ls[t],x);
	return colornum[ls[t]]+ask(rs[t],x-qiunum[ls[t]]);
}
inline void dfs(int x,int keep,int pre){
	landenum++;
	for(register int i=0;i<son[x].size();i++){
		int y=son[x][i];
		if(y==pre||y==ma[x]) continue;
		dfs(y,0,x);
	}
	if(ma[x]) dfs(ma[x],1,x);
	for(register int i=0;i<point[x].size();i++)
		add(1,1,m,point[x][i],1);
	for(register int i=0;i<son[x].size();i++){
		int y=son[x][i];
		if(y==pre||y==ma[x]) continue;
		go_add(son[x][i],x,1);
	}
	ans[x]=ask(1,tmp[x]);
	if(!keep){
		f[1]=1,qiunum[1]=0,colornum[1]=0;
	}
}
int main(){
	scanf("%d",&n);
	int  x,y;
	for(register int i=1;i<n;i++){
		scanf("%d%d",&x,&y);
		son[x].push_back(y);
		son[y].push_back(x);
	}
	for(register int i=1;i<=n;i++)
		scanf("%d",&tmp[i]);
	scanf("%d",&m);
	build(root,1,m);
	for(register int i=1;i<=m;i++){
		scanf("%d%d",&x,&y);
		w[i]=key[i]=y;
		point[x].push_back(i);
	}
	cout<<endl;
	sort(key+1,key+m+1);
	int q=unique(key+1,key+m+1)-key- 1;
	for(register int i=1;i<=m;i++){
		w[i]=lower_bound(key+1,key+q+1,w[i])-key;
	}
	dfs1(1,0);
	dfs(1,0,0);
	scanf("%d",&Q);
	while(Q--){
		scanf("%d",&x);
		printf("%d\n",ans[x]);
	}
	return 0;
}

 补充:一颗线段树的做法中一定要把vector清掉,虽然O(N)费时间,但如果vector过于膨胀,会暴掉...

posted @ 2019-07-31 14:22  bootpuss  阅读(213)  评论(0编辑  收藏  举报