HUT 线段树练习 部分题解
Posted 此剑之势愈斩愈烈
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HUT 线段树练习 部分题解相关的知识,希望对你有一定的参考价值。
F-poj 2886
这题去年月神给我们14级的抓过。然而比较偏数学。
题意大概是n个小朋友围成一圈,每个小朋友手里都有一张卡片,卡片上有个数字a[i]。
从第k个小朋友开始,第k个小朋友出圈,然后让他的左手方向的第a[k]个小朋友出圈。然后这个小朋友又根据规则让另一个小朋友出圈。
第p个出圈的小朋友拿到的糖果数目为p的因子个数,问谁拿到了最多的糖果
如果我们从1到n,逐个判断它的因子数目有多少个,效率非常低。其实我们可以通过规律直接dfs出小于等于n的,因子个数最多的数
因为要使因子个数尽量的多,该数应该满足如下性质,即可以表示为一系列连续质数的积,形如n=2^x1*3^x2*5^x3......
于是我们根据这个思路进行dfs。然后我们就得到是第几个出圈的人拿到最多的糖果啦。
之后我们要用线段树,求得拿到最多糖果的人是谁。
线段树维护区间当前的人数,然后把查询左手方向的第a[k]个小朋友,转化为查询剩余数组的第k的小朋友(取相对位置),就能直接在线段树中查询了
这里有关于反素数比较详细的介绍http://blog.csdn.net/ACdreamers/article/details/25049767
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=500008; int p[16]={2,3,5,7,11,13,17,19,23,29,31,37}; int mx,nx; void dfs(int num,int x,int y,int lim) { if(num>=9) return; if(mx<x) { mx=x; nx=y; } if(mx==x&&nx>y) nx=y; int i; for(i=1;i<=10;i++) { if(lim<y*p[num]) break; y*=p[num]; dfs(num+1,x*(i+1),y,lim); } } struct fuck{ int x; char s[15]; }f[maxn]; int a[maxn<<2]; void build(int x,int y,int c) { if(x==y) { a[c]=1; return; } int mid=(x+y)>>1; build(x,mid,c<<1); build(mid+1,y,c<<1|1); a[c]=a[c<<1]+a[c<<1|1]; } int update(int m,int x,int y,int c) { a[c]--; if(x==y) { return x; } int mid=(x+y)>>1; if(m<=a[c<<1]) return update(m,x,mid,c<<1); else return update(m-a[c<<1],mid+1,y,c<<1|1); } int main() { int i,j,n,m; while(scanf("%d%d",&n,&m)==2) { mx=nx=1; dfs(0,1,1,n); for(i=1;i<=n;i++) scanf("%s%d",f[i].s,&f[i].x); build(1,n,1); int k=n; int shit; for(i=1;i<=nx;i++) { k--; shit=update(m,1,n,1); if(k<=0) break; if(f[shit].x>0) m=(m-1+f[shit].x-1)%k+1; else m=((m+f[shit].x-1)%k+k)%k+1; } printf("%s %d\\n",f[shit].s,mx); } return 0; }
H - Mayor\'s posters
找一找可以发现别人转载的杭电老队长博客,里面有这题的详细介绍,关于离散化也有详细的讲解
http://www.cnblogs.com/Mu-Tou/archive/2011/08/11/2134427.html
I-hdu 1540 Tunnel Warfare
有可以看做一条线的村庄互相用地道连接。敌人会破坏一些村庄。八路军可以修好前一个被破坏的村庄。询问第k个村庄直接或间接连接了多少个村子。
区间合并问题。我的做法直接开两颗线段树。一颗存区间内最左边的,没有被破坏的村庄编号。一颗存区间内最右边的,没有被破坏的村庄编号,查询两次,将其相加即是答案。
#include<cstdio> #include<cstring> #include<algorithm> #include<queue> using namespace std; const int maxn=50008; const int INF=0x7f7f7f7f; int a[maxn<<4]; int b[maxn<<4]; int st[maxn]; int n; void build(int x,int y,int c) { if(x==y) { a[c]=n+1; b[c]=0; return; } int mid=(x+y)>>1; build(x,mid,c<<1); build(mid+1,y,c<<1|1); a[c]=min(a[c<<1],a[c<<1|1]); b[c]=max(b[c<<1],b[c<<1|1]); } void update(int sx,int sy,int x,int y,int c) { if(x==y) { if(sy==0) { b[c]=a[c]=x; } else { b[c]=0;a[c]=n+1; } return; } int mid=(x+y)>>1; if(sx<=mid) update(sx,sy,x,mid,c<<1); else update(sx,sy,mid+1,y,c<<1|1); a[c]=min(a[c<<1],a[c<<1|1]); b[c]=max(b[c<<1],b[c<<1|1]); } int yquery(int sx,int sy,int x,int y,int c) { if(sx<=x&&y<=sy) { return a[c]; } int mid=(x+y)>>1; int ans=n+1; if(sx<=mid) ans=min(ans,yquery(sx,sy,x,mid,c<<1)); if(sy>mid) ans=min(ans,yquery(sx,sy,mid+1,y,c<<1|1)); return ans; } int xquery(int sx,int sy,int x,int y,int c) { if(sx<=x&&y<=sy) { return b[c]; } int mid=(x+y)>>1; int ans=0; if(sx<=mid) ans=max(ans,xquery(sx,sy,x,mid,c<<1)); if(sy>mid) ans=max(ans,xquery(sx,sy,mid+1,y,c<<1|1)); return ans; } bool query(int sx,int x,int y,int c) { if(x==y) { if(a[c]==x) return true; return false; } int mid=(x+y)>>1; if(sx<=mid) return query(sx,x,mid,c<<1); else return query(sx,mid+1,y,c<<1|1); } int main() { int i,j,m; char s[5]; while(scanf("%d%d",&n,&m)==2) { build(1,n,1); int u; int top=0; while(m--) { scanf("%s",s); if(s[0]==\'R\') { update(st[--top],1,1,n,1); } else { scanf("%d",&u); if(s[0]==\'D\') { update(u,0,1,n,1); st[top++]=u; } else { int bitch; if(query(u,1,n,1)) bitch=0; else { bitch=1; if(u>1) bitch+=(u-xquery(1,u-1,1,n,1)-1); if(u<n) bitch+=(yquery(u+1,n,1,n,1)-u-1); } printf("%d\\n",bitch); } } } } return 0; }
J-light oj 1120
扫描线问题需要会离散化,区间更新。而且这里的区间更新非常灵活,不是pushdown,而是pushup。根据需要解锁不同姿势。。。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define ll __int64 const int maxn=30008; struct fuck{ int x,y,u,v; int idx; int w; bool operator<(const fuck &a) const { return idx<a.idx; } }f[maxn<<1]; int tol; int re; int a[maxn<<1]; int b[maxn<<1]; ll st[maxn<<4]; int lazy[maxn<<4]; int shit[maxn]; int bs(int x) { int left,right; left=1;right=re; while(left<=right) { int mid=(left+right)>>1; if(b[mid]<x) left=mid+1; else right=mid-1; } return left; } void init() { memset(st,0,sizeof(st)); memset(lazy,0,sizeof(lazy)); } void pushup(int c,int x,int y) { if(lazy[c]) st[c]=b[y+1]-b[x]; else if(x==y) st[c]=0; else st[c]=st[c<<1]+st[c<<1|1]; } void update(int sx,int sy,int w,int x,int y,int c) { if(sx>sy) return; if(sx<=x&&y<=sy) { lazy[c]+=w; pushup(c,x,y); return; } int mid=(x+y)>>1; if(sx<=mid) update(sx,sy,w,x,mid,c<<1); if(sy>mid) update(sx,sy,w,mid+1,y,c<<1|1); pushup(c,x,y); return; } int main() { int i,j,t,n,m,x,y,sx,sy,u,v; int cas=1; scanf("%d",&t); while(t--) { scanf("%d",&n); tol=0; for(i=1;i<=n;i++) { scanf("%d%d%d%d",&x,&y,&sx,&sy); f[++tol].idx=x;f[tol].x=y;f[tol].y=sy; a[tol]=y;f[tol].w=1; f[++tol].idx=sx;f[tol].x=y;f[tol].y=sy; a[tol]=sy;f[tol].w=-1; } sort(f+1,f+tol+1); sort(a+1,a+tol+1); // printf("%d\\n",tol); a[0]=-1;re=0; for(i=1;i<=tol;i++) if(a[i]!=a[i-1]) b[++re]=a[i]; ll sum=0; init(); for(i=1;i<=tol;i++) { u=bs(f[i].x); v=bs(f[i].y); //printf("%d %d\\n",u,v); if(i==1) update(u,v-1,f[i].w,1,re,1); else { // printf("%d\\n",st[1]); sum+=st[1]*((ll)f[i].idx-(ll)f[i-1].idx); update(u,v-1,f[i].w,1,re,1); } } printf("Case %d: %lld\\n",cas++,sum); } return 0; }
L-uvalive 4730 kingdom
大白薯上的题。抓这题是liji的主意。线段树+并查集。
在二维坐标上有一系列的点,每个点代表一个城市。进行一系列的操作。一种操作是将两个城市相连接,互通的城市形成一个州。另一个操作是询问y直线一共穿过了多少州和城市
用并查集维护州的相连情况,然后分类讨论,更新y轴的城市和州分布情况,又是区间更新加离散化orz。
分类讨论其实很简单。你需要仔细想想,什么情况下点y的州数目和城市数目变化,什么情况不变化,按照思路更新即可。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define db double const int maxn=200008; struct poi{ int x,y; int idx; bool operator<(const poi &a) const { return y<a.y; } }f[maxn]; int g[maxn]; int rel[maxn]; int a[maxn<<4],b[maxn<<4]; int lazya[maxn<<4],lazyb[maxn<<4]; struct fuck{ int next; int x,y; int w; }uni[maxn]; void init(int n) { memset(a,0,sizeof(a)); memset(b,0,sizeof(b)); memset(lazya,0,sizeof(lazya)); memset(lazyb,0,sizeof(lazyb)); for(int i=1;i<=n;i++) { uni[i].next=i; uni[i].x=uni[i].y=rel[i]; uni[i].w=1; } } int find(int u) { if(u==uni[u].next) return u; return uni[u].next=find(uni[u].next); } void pushdowna(int c) { if(lazya[c]==0) return; lazya[c<<1]+=lazya[c]; lazya[c<<1|1]+=lazya[c]; a[c<<1]+=lazya[c]; a[c<<1|1]+=lazya[c]; lazya[c]=0; } void pushdownb(int c) { if(lazyb[c]==0) return; lazyb[c<<1]+=lazyb[c]; lazyb[c<<1|1]+=lazyb[c]; b[c<<1]+=lazyb[c]; b[c<<1|1]+=lazyb[c]; lazyb[c]=0; } void update(int sx,int sy,int w,int x,int y,int c) { if(sx>sy) return; if(sx<=x&&y<=sy) { a[c]+=w; lazya[c]+=w; return; } pushdowna(c); int mid=(x+y)>>1; if(sx<=mid) update(sx,sy,w,x,mid,c<<1); if(mid<sy) update(sx,sy,w,mid+1,y,c<<1|1); return; } void updateb(int sx,int sy,int w,int x,int y,int c) { if(sx>sy) return; if(sx<=x&&y<=sy) { b[c]+=w; lazyb[c]+=w; return; } pushdownb(c); int mid=(x+y)>>1; if(sx<=mid) updateb(sx,sy,w,x,mid,c<<1); if(mid<sy) updateb(sx,sy,w,mid+1,y,c<<1|1); return; } int bs(db shit,int n) { int left,right; left=1;right=n; while(left<=right) { int mid=(left+right)>>1; if(f[mid].y<shit) left=mid+1; else right=mid-1; } return left-1; } int query(int sx,int x,int y,int c) { if(x==y) { return a[c]; } pushdowna(c); int mid=(x+y)>>1; if(sx<=mid) return query(sx,x,mid,c<<1); else return query(sx,mid+1,y,c<<1|1); } int queryb(int sx,int x,int y,int c) { if(x==y) { return b[c]; } pushdownb(c); int mid=(x+y)>>1; if(sx<=mid) return queryb(sx,x,mid,c<<1); else return queryb(sx,mid+1,y,c<<1|1); } int main() { int i,j,t,n,m,u,v,x,y; scanf("%d",&t); char s[10]; while(t--) { scanf("%d",&n); for(i=1;i<=n;i++) { scanf("%d%d",&f[i].x,&f[i].y); f[i].idx=i; } sort(f+1,f+1+n); for(i=1;i<=n;i++) g[f[i].idx]=i; int pre=-1,k=0; for(i=1;i<=n;i++) { if(f[i].y!=pre) rel[i]=++k; else rel[i]=k; pre=f[i].y; } init(n); scanf("%d",&m); while(m--) { scanf("%s",s); if(s[0]==\'r\') { scanf("%d%d",&u,&v); u++;v++; u=g[u];v=g[v]; x=find(u);y=find(v); if(x!=y) { if(uni[x].y>uni[y].y){ int shit=x;x=y;y=shit; } if(uni[x].y<uni[y].x) { update(uni[x].y,uni[y].x-1,1,1,k,1); updateb(uni[x].y,uni[y].x-1,uni[y].w+uni[x].w ,1,k,1); updateb(uni[x].x,uni[x].y-1,uni[y].w,1,k,1); updateb(uni[y].x,uni[y].y-1,uni[x].w,1,k,1); } else { update(max(uni[y].x,uni[x].x), uni[x].y-1,-1,1,k,1); updateb(uni[x].x,uni[y].x-1,uni[y].w,1,k,1); updateb(uni[y].x,uni[x].x-1,uni[x].w,1,k,1); updateb(uni[x].y,uni[y].y-1,uni[x].w,1,k,1); } uni[x].next=y; uni[y].w+=uni[x].w; uni[y].x=min(uni[x].x,uni[y].x); } } else { db shit; scanf("%lf",&shit); if(shit<f[1].y) { printf("0 0\\n"); continue; } x=bs(shit,n); x=rel[x]; int sx=query(x,1,k,1); int sy=queryb(x,1,k,1); printf("%d %d\\n",sx,sy); } } } return 0; }
以上是关于HUT 线段树练习 部分题解的主要内容,如果未能解决你的问题,请参考以下文章