模板(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过于膨胀,会暴掉...