BZOJ3757: 苹果树
题目:http://www.lydsy.com/JudgeOnline/problem.php?id=3757
题解:
颜色种数不太好用树形数据结构维护,因为子节点的信息不能快速合并为父节点的信息。而莫队算法正是解决这类题目的利器。
节点与节点间的转移可以戳vfleaking的blog:http://vfleaking.blog.163.com/blog/static/174807634201311011201627/
比较巧妙的一点是把麻烦的lca去掉,然后统计答案的时候加上,统计完了再删去。
其实我做这题是为了测试各种树分块的快慢。。。
#1 按王室联邦的分块方法,可以保证所有的块内距离<=sqrt(n),最多有一块的大小>sqrt(n)并且<3sqrt(n)。块内可能是不连通的。
但这对我们算法的执行并没有什么影响,因为它只是作为莫队算法排序的参照(可以说是估价函数)。
块大小sqrt(n) 耗时 8744ms
块大小sqrt(n*log2(n)) 耗时 7520ms
1 #include<cstdio> 2 3 #include<cstdlib> 4 5 #include<cmath> 6 7 #include<cstring> 8 9 #include<algorithm> 10 11 #include<iostream> 12 13 #include<vector> 14 15 #include<map> 16 17 #include<set> 18 19 #include<queue> 20 21 #include<string> 22 23 #define inf 1000000000 24 25 #define maxn 200000+5 26 27 #define maxm 200000+5 28 29 #define eps 1e-10 30 31 #define ll long long 32 33 #define pa pair<int,int> 34 35 #define for0(i,n) for(int i=0;i<=(n);i++) 36 37 #define for1(i,n) for(int i=1;i<=(n);i++) 38 39 #define for2(i,x,y) for(int i=(x);i<=(y);i++) 40 41 #define for3(i,x,y) for(int i=(x);i>=(y);i--) 42 43 #define for4(i,x) for(int i=head[x],y=e[i].go;i;i=e[i].next,y=e[i].go) 44 45 #define for5(n,m) for(int i=1;i<=n;i++)for(int j=1;j<=m;j++) 46 47 #define mod 1000000007 48 49 using namespace std; 50 51 inline int read() 52 53 { 54 55 int x=0,f=1;char ch=getchar(); 56 57 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 58 59 while(ch>='0'&&ch<='9'){x=10*x+ch-'0';ch=getchar();} 60 61 return x*f; 62 63 } 64 int n,m,size,top,cnt,tot,rt,ti,ret; 65 int a[maxn],head[maxn],ans[maxn],s[maxn],b[maxn],pos[maxn],sta[maxn],f[maxn][18],dep[maxn]; 66 struct rec{int l,r,x,y,id;}q[maxn]; 67 struct edge{int go,next;}e[2*maxn]; 68 bool v[maxn]; 69 inline void add(int x,int y) 70 { 71 e[++tot]=(edge){y,head[x]};head[x]=tot; 72 e[++tot]=(edge){x,head[y]};head[y]=tot; 73 } 74 inline void dfs(int x) 75 { 76 pos[x]=++ti;int tmp=top; 77 for1(i,17)if(dep[x]>=1<<i)f[x][i]=f[f[x][i-1]][i-1];else break; 78 for4(i,x)if(y!=f[x][0]) 79 { 80 f[y][0]=x;dep[y]=dep[x]+1; 81 dfs(y); 82 if(top-tmp>=size) 83 { 84 ++cnt; 85 while(top!=tmp)b[sta[top--]]=cnt; 86 } 87 } 88 sta[++top]=x; 89 } 90 inline int lca(int x,int y) 91 { 92 if(dep[x]<dep[y])swap(x,y); 93 int t=dep[x]-dep[y]; 94 for0(i,17)if(t&(1<<i))x=f[x][i]; 95 if(x==y)return x; 96 for3(i,17,0)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i]; 97 return f[x][0]; 98 } 99 inline void change(int x) 100 { 101 if(v[x]){s[a[x]]--;if(!s[a[x]])ret--;} 102 else {s[a[x]]++;if(s[a[x]]==1)ret++;} 103 v[x]^=1; 104 } 105 inline void work(int x,int y) 106 { 107 while(x!=y) 108 { 109 if(dep[x]<dep[y])swap(x,y); 110 change(x); 111 x=f[x][0]; 112 } 113 } 114 inline bool cmp(rec x,rec y){return b[x.l]==b[y.l]?pos[x.r]<pos[y.r]:b[x.l]<b[y.l];} 115 116 int main() 117 118 { 119 120 freopen("input.txt","r",stdin); 121 122 freopen("output.txt","w",stdout); 123 n=read();m=read(); 124 for1(i,n)a[i]=read(); 125 for1(i,n){int x=read(),y=read();add(x,y);if(x*y==0)rt=x+y;} 126 size=sqrt(n); 127 dfs(rt); 128 while(top)b[sta[top--]]=cnt; 129 for1(i,m) 130 { 131 q[i].l=read(),q[i].r=read(),q[i].x=read(),q[i].y=read(),q[i].id=i; 132 if(b[q[i].l]>b[q[i].r])swap(q[i].l,q[i].r); 133 } 134 sort(q+1,q+m+1,cmp); 135 int l=rt,r=rt; 136 for1(i,m) 137 { 138 work(l,q[i].l);work(r,q[i].r); 139 l=q[i].l;r=q[i].r;int f=lca(l,r); 140 change(f); 141 ans[q[i].id]=ret-(int)(q[i].x!=q[i].y&&s[q[i].x]&&s[q[i].y]); 142 change(f); 143 } 144 for1(i,m)printf("%d\n",ans[i]); 145 146 return 0; 147 148 }
#2 按照块状树的分块方法,如果一个节点父节点所在块的大小还没有达到sqrt(n),就把该点塞入父亲所在块,否则另开一块。
这样可以保证块内的所有点都是连通的。但是由于是从上到下分,所以会被菊花图卡到块数为O(n)
块大小sqrt(n) 耗时 9448ms
块大小sqrt(n*log2(n)) 耗时 7180ms
1 #include<cstdio> 2 3 #include<cstdlib> 4 5 #include<cmath> 6 7 #include<cstring> 8 9 #include<algorithm> 10 11 #include<iostream> 12 13 #include<vector> 14 15 #include<map> 16 17 #include<set> 18 19 #include<queue> 20 21 #include<string> 22 23 #define inf 1000000000 24 25 #define maxn 200000+5 26 27 #define maxm 200000+5 28 29 #define eps 1e-10 30 31 #define ll long long 32 33 #define pa pair<int,int> 34 35 #define for0(i,n) for(int i=0;i<=(n);i++) 36 37 #define for1(i,n) for(int i=1;i<=(n);i++) 38 39 #define for2(i,x,y) for(int i=(x);i<=(y);i++) 40 41 #define for3(i,x,y) for(int i=(x);i>=(y);i--) 42 43 #define for4(i,x) for(int i=head[x],y=e[i].go;i;i=e[i].next,y=e[i].go) 44 45 #define for5(n,m) for(int i=1;i<=n;i++)for(int j=1;j<=m;j++) 46 47 #define mod 1000000007 48 49 using namespace std; 50 51 inline int read() 52 53 { 54 55 int x=0,f=1;char ch=getchar(); 56 57 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 58 59 while(ch>='0'&&ch<='9'){x=10*x+ch-'0';ch=getchar();} 60 61 return x*f; 62 63 } 64 int n,m,size,top,cnt,tot,rt,ti,ret; 65 int a[maxn],head[maxn],sum[maxn],ans[maxn],s[maxn],b[maxn],pos[maxn],sta[maxn],f[maxn][18],dep[maxn]; 66 struct rec{int l,r,x,y,id;}q[maxn]; 67 struct edge{int go,next;}e[2*maxn]; 68 bool v[maxn]; 69 inline void add(int x,int y) 70 { 71 e[++tot]=(edge){y,head[x]};head[x]=tot; 72 e[++tot]=(edge){x,head[y]};head[y]=tot; 73 } 74 inline void dfs(int x) 75 { 76 pos[x]=++ti; 77 for1(i,17)if(dep[x]>=1<<i)f[x][i]=f[f[x][i-1]][i-1];else break; 78 for4(i,x)if(y!=f[x][0]) 79 { 80 f[y][0]=x;dep[y]=dep[x]+1; 81 sum[b[y]=sum[b[x]]<size?b[x]:++cnt]++; 82 dfs(y); 83 } 84 } 85 inline int lca(int x,int y) 86 { 87 if(dep[x]<dep[y])swap(x,y); 88 int t=dep[x]-dep[y]; 89 for0(i,17)if(t&(1<<i))x=f[x][i]; 90 if(x==y)return x; 91 for3(i,17,0)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i]; 92 return f[x][0]; 93 } 94 inline void change(int x) 95 { 96 if(v[x]){s[a[x]]--;if(!s[a[x]])ret--;} 97 else {s[a[x]]++;if(s[a[x]]==1)ret++;} 98 v[x]^=1; 99 } 100 inline void work(int x,int y) 101 { 102 while(x!=y) 103 { 104 if(dep[x]<dep[y])swap(x,y); 105 change(x); 106 x=f[x][0]; 107 } 108 } 109 inline bool cmp(rec x,rec y){return b[x.l]==b[y.l]?pos[x.r]<pos[y.r]:b[x.l]<b[y.l];} 110 111 int main() 112 113 { 114 115 freopen("input.txt","r",stdin); 116 117 freopen("output.txt","w",stdout); 118 n=read();m=read(); 119 for1(i,n)a[i]=read(); 120 for1(i,n){int x=read(),y=read();add(x,y);if(x*y==0)rt=x+y;} 121 size=sqrt(n); 122 sum[1]=b[1]=cnt=1; 123 dfs(rt); 124 for1(i,m) 125 { 126 q[i].l=read(),q[i].r=read(),q[i].x=read(),q[i].y=read(),q[i].id=i; 127 if(b[q[i].l]>b[q[i].r])swap(q[i].l,q[i].r); 128 } 129 sort(q+1,q+m+1,cmp); 130 int l=rt,r=rt; 131 for1(i,m) 132 { 133 work(l,q[i].l);work(r,q[i].r); 134 l=q[i].l;r=q[i].r;int f=lca(l,r); 135 change(f); 136 ans[q[i].id]=ret-(int)(q[i].x!=q[i].y&&s[q[i].x]&&s[q[i].y]); 137 change(f); 138 } 139 for1(i,m)printf("%d\n",ans[i]); 140 141 return 0; 142 143 }
#3 直接把dfs序拉出来,做序列上的分块。这样有可能块内两点之间离了很远。感觉效果不会很好,但是好写。。。
耗时 16800ms
1 #include<cstdio> 2 3 #include<cstdlib> 4 5 #include<cmath> 6 7 #include<cstring> 8 9 #include<algorithm> 10 11 #include<iostream> 12 13 #include<vector> 14 15 #include<map> 16 17 #include<set> 18 19 #include<queue> 20 21 #include<string> 22 23 #define inf 1000000000 24 25 #define maxn 200000+5 26 27 #define maxm 200000+5 28 29 #define eps 1e-10 30 31 #define ll long long 32 33 #define pa pair<int,int> 34 35 #define for0(i,n) for(int i=0;i<=(n);i++) 36 37 #define for1(i,n) for(int i=1;i<=(n);i++) 38 39 #define for2(i,x,y) for(int i=(x);i<=(y);i++) 40 41 #define for3(i,x,y) for(int i=(x);i>=(y);i--) 42 43 #define for4(i,x) for(int i=head[x],y=e[i].go;i;i=e[i].next,y=e[i].go) 44 45 #define for5(n,m) for(int i=1;i<=n;i++)for(int j=1;j<=m;j++) 46 47 #define mod 1000000007 48 49 using namespace std; 50 51 inline int read() 52 53 { 54 55 int x=0,f=1;char ch=getchar(); 56 57 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 58 59 while(ch>='0'&&ch<='9'){x=10*x+ch-'0';ch=getchar();} 60 61 return x*f; 62 63 } 64 int n,m,size,top,cnt,tot,rt,ti,ret; 65 int a[maxn],head[maxn],sum[maxn],ans[maxn],s[maxn],b[maxn],pos[maxn],sta[maxn],f[maxn][18],dep[maxn]; 66 struct rec{int l,r,x,y,id;}q[maxn]; 67 struct edge{int go,next;}e[2*maxn]; 68 bool v[maxn]; 69 inline void add(int x,int y) 70 { 71 e[++tot]=(edge){y,head[x]};head[x]=tot; 72 e[++tot]=(edge){x,head[y]};head[y]=tot; 73 } 74 inline void dfs(int x) 75 { 76 pos[x]=++ti; 77 for1(i,17)if(dep[x]>=1<<i)f[x][i]=f[f[x][i-1]][i-1];else break; 78 for4(i,x)if(y!=f[x][0]) 79 { 80 f[y][0]=x;dep[y]=dep[x]+1; 81 dfs(y); 82 } 83 } 84 inline int lca(int x,int y) 85 { 86 if(dep[x]<dep[y])swap(x,y); 87 int t=dep[x]-dep[y]; 88 for0(i,17)if(t&(1<<i))x=f[x][i]; 89 if(x==y)return x; 90 for3(i,17,0)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i]; 91 return f[x][0]; 92 } 93 inline void change(int x) 94 { 95 if(v[x]){s[a[x]]--;if(!s[a[x]])ret--;} 96 else {s[a[x]]++;if(s[a[x]]==1)ret++;} 97 v[x]^=1; 98 } 99 inline void work(int x,int y) 100 { 101 while(x!=y) 102 { 103 if(dep[x]<dep[y])swap(x,y); 104 change(x); 105 x=f[x][0]; 106 } 107 } 108 inline bool cmp(rec x,rec y){return b[pos[x.l]]==b[pos[y.l]]?pos[x.r]<pos[y.r]:b[pos[x.l]]<b[pos[y.l]];} 109 110 int main() 111 112 { 113 114 freopen("input.txt","r",stdin); 115 116 freopen("output.txt","w",stdout); 117 n=read();m=read(); 118 for1(i,n)a[i]=read(); 119 for1(i,n){int x=read(),y=read();add(x,y);if(x*y==0)rt=x+y;} 120 size=sqrt(n); 121 dfs(rt); 122 for1(i,n)b[i]=(i-1)/size+1; 123 for1(i,m) 124 { 125 q[i].l=read(),q[i].r=read(),q[i].x=read(),q[i].y=read(),q[i].id=i; 126 if(b[q[i].l]>b[q[i].r])swap(q[i].l,q[i].r); 127 } 128 sort(q+1,q+m+1,cmp); 129 int l=rt,r=rt; 130 for1(i,m) 131 { 132 work(l,q[i].l);work(r,q[i].r); 133 l=q[i].l;r=q[i].r;int f=lca(l,r); 134 change(f); 135 ans[q[i].id]=ret-(int)(q[i].x!=q[i].y&&s[q[i].x]&&s[q[i].y]); 136 change(f); 137 } 138 for1(i,m)printf("%d\n",ans[i]); 139 140 return 0; 141 142 }
按dfs序分块果然是做死。。。七爷有更好的办法?
从此AC率是路人T_T