标签:continue getc sort ++i ack 启发式合并 vector 补充 ora
谢天谢地!
首先鸣谢人帅话骚的好心人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过于膨胀,会暴掉...
标签:continue getc sort ++i ack 启发式合并 vector 补充 ora
原文地址:https://www.cnblogs.com/2018hzoicyf/p/11276015.html