@Asuna
2017-03-29T12:51:32.000000Z
字数 8329
阅读 926
题解
题意:一个队列,要求满足能单点加减和区间和查询。
题解:裸线段树,update改变单点值即可。
参考代码:
#include<iostream>#include<cstdio>#include<cstring>#include<cmath>#include<algorithm>using namespace std;struct node{int l,r,sum,mid;}f[1000005];int a[100000],ans,t,n,d=1,x,y;char s[16];void uproot(int n){f[n].sum=f[2*n].sum+f[2*n+1].sum;}void built(int l,int r,int n){f[n].mid=(l+r)/2;f[n].r=r;f[n].l=l;f[n].sum=0;if (l==r){f[n].sum=a[l];return ;}built(l,f[n].mid,2*n);built(f[n].mid+1,r,2*n+1);uproot(n);}void update(int c,int q,int val){if (f[c].l==q && f[c].r==q){f[c].sum+=val;return ;}if (q<=f[c].mid) update(2*c,q,val);else update(2*c+1,q,val);uproot(c);}void query(int c,int a,int b){//cout<<c<<endl;// cout<<f[c].l<<" "<<f[c].r<<endl;// cout<<" "<<a<<" "<<b<<endl;if (f[c].l>=a && f[c].r<=b){// cout<<"!!!"<<endl;ans+=f[c].sum;//cout<<ans<<endl;return ;//cout<<"?????"<<endl;}if (b<=f[c].mid) query(2*c,a,b);else if (a>f[c].mid) query(2*c+1,a,b);else{query(2*c,a,f[c].mid);query(2*c+1,f[c].mid+1,b);}}int main(){ios::sync_with_stdio(false);cin>>t;while (t--){cin>>n;for (int i=1; i<=n; i++)cin>>a[i];built(1,n,1);printf("Case %d:\n",d++);//cout<<"Case "<<d++<<":"<<endl;while (cin>>s){//cout<<s<<endl;if (s[0]=='E') break;cin>>x>>y;ans=0;if (s[0]=='A') update(1,x,y);else if(s[0]=='S') update(1,x,-y);else if(s[0]=='Q'){query(1,x,y);//cout<<ans<<endl;printf("%d\n",ans);}}}return 0;}
题意:一个队列,要求完成区间+1和单点查询。
题解:可以用线段树的段更新点询问,每次把a,b全部+1,最后全部查询一遍就行了。
参考代码:
#include<iostream>#include<cstdio>using namespace std;struct node{int l,r,cnt;}p[1000010];int mid,n,a,b;void build(int l,int r,int rt){p[rt].l=l;p[rt].r=r;p[rt].cnt=0;if (l==r) return;int mid=(l+r)/2;build(l,mid,rt*2);build(mid+1,r,rt*2+1);}void update(int l,int r,int rt){if (l==p[rt].l && r==p[rt].r){p[rt].cnt++;return;}int mid=(p[rt].l+p[rt].r)/2;if (r<=mid) update(l,r,rt*2);else if (l>mid) update(l,r,rt*2+1);else{update(l,mid,rt*2);update(mid+1,r,rt*2+1);}}int query(int i,int rt){if (p[rt].l==p[rt].r) return p[rt].cnt;if (p[rt].cnt){p[rt*2].cnt+=p[rt].cnt;p[rt*2+1].cnt+=p[rt].cnt;p[rt].cnt=0;}mid=(p[rt].l+p[rt].r)/2;if (i<=mid)return query(i,rt*2);elsereturn query(i,rt*2+1);}int main(){while (cin>>n){if (n==0) break;build(1,n,1);for (int i=1; i<=n; i++){cin>>a>>b;update(a,b,1);}for (int i=1; i<n; i++)cout<<query(i,1)<<" ";cout<<query(n,1)<<endl;}return 0;}
题意:有n张海报,按给出的顺序依次贴在一面墙上,第i张海报的左右端点为li和ri,问最终能看到几张不同的海报。
题解:由于li和ri的范围太大(大的超出想象),于是看了js的题解之后才知道可以离散化解决,也就是通过压缩没数据的空出空间来缩小li和ri的范围,比如[1,2],[2,3],[1,5],[4,9],离散化就是先记录每个端点对应的区间,然后对每个端点排序,排序后为1,1,2,2,3,4,5,9。之后从1~9进行压缩,这里1,2,3,4,5都有数,因此无法压缩,只有9可以压缩成6,然后放回原先区间则离散化后的区间为[1,2],[2,3],[1,5],[4,6]。在离散化之后只需要用线段树从后往前贴,查询到某块区域被完全覆盖了就返回即可。
参考代码:
#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;int flag,n,t,ans;struct node{int l,r;bool vis;//判断是不是完全覆盖}tree[400010];struct point{int id,x;}a[400010];int cmp1(point a,point b){return a.x<b.x;}int cmp2(point a,point b){if (a.id==b.id)return a.x<b.x;return a.id>b.id;}void PushUp(int rt){tree[rt].vis=tree[(rt*2)].vis && tree[(rt*2+1)].vis;}void build(int l,int r,int rt){tree[rt].l=l;tree[rt].r=r;tree[rt].vis=0;if (tree[rt].l==tree[rt].r) return;int mid=(l+r)/2;build(l,mid,(rt*2));build(mid+1,r,(rt*2+1));}void query(int l,int r,int rt){if (tree[rt].vis) return ;if (tree[rt].l==l && tree[rt].r==r){tree[rt].vis=1;flag=1;return ;}int mid=(tree[rt].l+tree[rt].r)/2;if (r<=mid)query(l,r,(rt*2));elseif (l>=mid+1)query(l,r,(rt*2+1));else{query(l,mid,(rt*2));query(mid+1,r,(rt*2+1));}PushUp(rt);}int main(){cin>>t;while (t--){cin>>n;for (int i=0; i<2*n; i+=2){scanf("%d%d",&a[i].x,&a[i+1].x);a[i].id=a[i+1].id=i;}sort(a,a+2*n,cmp1);int tot=0,pre=0;//离散for(int i=0; i<2*n; i++){if (a[i].x==pre) a[i].x=tot;else{pre=a[i].x;tot++;a[i].x=tot;}}build(1,2*n,1);sort(a,a+2*n,cmp2); //排序,从后往前贴ans=0;for(int i=0;i<2*n;i+=2){int l=a[i].x,r=a[i+1].x;flag=0;query(l,r,1);if (flag) ans++;}cout<<ans<<endl;}return 0;}
题意:一条线上有n个点从1~n。
D x是破坏x这个点。
Q x是表示查询以x所在的最长的连续的点的个数
R是恢复上一次破坏的点。
题解:设置一个lc记录区间左端点开始的最大连续个数,rc 记录区间右端点开始的最大的连续个数,sc表示该区间最大的连续点的个数。多维护一个栈来保存被破坏的点,以便于之后可能的修复。每次查询分别往左右子树找最大连续个数最后用max(lc,rc)来更新sc。
参考代码:
#include<iostream>#include<cstring>#include<cstdio>#include<algorithm>#include<stack>using namespace std;int lc[400005],rc[400005],sc[400005],n,m,x;char c;stack<int> a;void uppush(int s,int m){lc[s]=lc[s*2];rc[s]=rc[s*2+1];if (lc[s]==m-(m/2)) lc[s]+=lc[s*2+1];if (rc[s]==m/2) rc[s]+=rc[s*2];sc[s]=max(lc[s*2+1]+rc[s*2],max(sc[s*2],sc[s*2+1]));}void build(int l,int r,int s){lc[s]=rc[s]=sc[s]=r-l+1;if (l==r) return ;int mid=(l+r)/2;build(l,mid,s*2);build(mid+1,r,s*2+1);}void update(int p,int c,int l,int r,int s){if (l==r){if (c){lc[s]=rc[s]=sc[s]=1;return ;}else{lc[s]=rc[s]=sc[s]=0;return ;}}int mid=(l+r)/2;if (p<=mid) update(p,c,l,mid,s*2);else update(p,c,mid+1,r,s*2+1);uppush(s,r-l+1);}int query(int p,int l,int r,int s){if (l==r || sc[s]==0 || sc[s]==r-l+1)return sc[s];int mid=(l+r)/2;if (p<=mid){if (p>=mid-rc[s*2]+1)return rc[s*2]+query(mid+1,mid+1,r,s*2+1);elsereturn query(p,l,mid,s*2);}else{if (p<=mid+lc[s*2+1])return lc[s*2+1]+query(mid,l,mid,s*2);else return query(p,mid+1,r,s*2+1);}}int main(){while(cin>>n>>m){build(1,n,1);while (m--){//getchar();cin>>c;if (c!='R')cin>>x;if (c=='D'){update(x,0,1,n,1);a.push(x);}if (c=='R'){update(a.top(),1,1,n,1);a.pop();}if (c=='Q') cout<<query(x,1,n,1)<<endl;}}return 0;}
题意:给定一个数列,有如下两个操作:
(1)C a b c,表示第a个到第b个数全部加上c
(2)Q a b,表示询问第a个数到b个数的和
题解:普通的区间修改和区间和询问。注意范围可能比int大,要用long long统计结果。
参考代码:
#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;int num[100005],t,n,x,y,z;long long sum[400005],a[400005];char c[2];void uproot(int s){sum[s]=sum[s*2]+sum[s*2+1];}void pushdown(int s,int l){if (a[s]){a[s*2]+=a[s];a[s*2+1]+=a[s];sum[s*2]+=a[s]*(l-(l/2));sum[s*2+1]+=a[s]*(l/2);a[s]=0;}}void build(int s,int l,int r){if (l==r){cin>>sum[s];return ;}int mid=(l+r)/2;build(s*2,l,mid);build(s*2+1,mid+1,r);uproot(s);}void update(int c,int x,int y,int l,int r,int s){if (l>=x && r<=y){a[s]+=c;sum[s]+=c*(r-l+1);return ;}pushdown(s,r-l+1);int mid=(l+r)/2;if (x<=mid)update(c,x,y,l,mid,s*2);if (y>mid)update(c,x,y,mid+1,r,s*2+1);uproot(s);}long long query(int x,int y,int l,int r,int s){if (l>=x && r<=y)return sum[s];pushdown(s,r-l+1);int mid=(l+r)>>1;long long ans=0;if(x<=mid)ans+=query(x,y,l,mid,s*2);if(y>mid)ans+=query(x,y,mid+1,r,s*2+1);return ans;}int main(){std::ios::sync_with_stdio(false);cin>>n>>t;build(1,1,n);while(t--){cin>>c;if (c[0]=='C'){cin>>x>>y>>z;update(z,x,y,1,n,1);}else{cin>>x>>y;cout<<query(x,y,1,n,1)<<endl;}}return 0;}
题意:给定一个数列,有如下两种操作:
(1)1 l r x,表示第l个到第r个数全部加上x
(2)2 l r,表示询问第l个数到r个数对应斐波那契的第ai项的和,输出mod10^9+7。
题解:呃啊。。寒假的时候看到这题巨烦。。线段树+斐波那契简直恶心就没做。。现在想来应该是线段树+矩阵快速幂(别问我为什么想到矩阵。。因为它是斐波那契。。)总的来说就是维护一个经典的斐波那契矩阵(2*2的(1,0,0,1))。于是用线段树存储矩阵,每次更新加x就可以用乘以(1,0,0,1)^x代替
参考代码:
#include<iostream>#include<cstdio>#include<cstring>#include<cstdlib>#include<cmath>#include<algorithm>using namespace std;const long long mod=1e9+7;int n,m,op,a,b;long long x;struct Mat{long long x[2][2];void init(){x[0][0]=x[1][1]=1;x[1][0]=x[0][1]=0;}Mat operator*(const Mat& m2)const{Mat m;m.x[0][0]=m.x[0][1]=m.x[1][0]=m.x[1][1]=0;for(int k=0;k<2;k++)for(int i=0;i<2;i++)for(int j=0;j<2;j++)m.x[i][j]=(m.x[i][j]+x[i][k]*m2.x[k][j])%mod;return m;}Mat operator+(const Mat& m2)const{Mat m;for(int i=0;i<2;i++)for(int j=0;j<2;j++)m.x[i][j]=(x[i][j]+m2.x[i][j])%mod;return m;}};Mat sum[400010],add[400010];Mat pow(long long n){Mat m,ret;m.x[0][0]=1;m.x[0][1]=1;m.x[1][0]=1;m.x[1][1]=0;ret.init();while(n){if (n&1) ret=ret*m;m=m*m;n>>=1;}return ret;}long long solve(long long n){Mat ret=pow(n);return ret.x[0][0];}void uppush(int rt){sum[rt]=sum[rt*2]+sum[rt*2+1];}void build(int l,int r,int rt){sum[rt].init();add[rt].init();if(l==r){long long x;cin>>x;sum[rt]=pow(x-1);return;}int m=(l+r)/2;build(l,m,rt*2);build(m+1,r,rt*2+1);uppush(rt);}void pushdown(int rt){sum[rt*2]=sum[rt*2]*add[rt];sum[rt*2+1]=sum[rt*2+1]*add[rt];add[rt*2]=add[rt*2]*add[rt];add[rt*2+1]=add[rt*2+1]*add[rt];add[rt].init();}void update(int L,int R,Mat c,int l,int r,int rt){if (L<=l && R>=r){sum[rt]=sum[rt]*c;add[rt]=add[rt]*c;return;}pushdown(rt);int m=(l+r)/2;if(L<=m) update(L,R,c,l,m,rt*2);if(R>m) update(L,R,c,m+1,r,rt*2+1);uppush(rt);}long long query(int L,int R,int l,int r,int rt){if (L<=l && R>=r){return sum[rt].x[0][0];}pushdown(rt);int mid=(l+r)/2;long long ret=0;if (L<=mid) ret=(ret+query(L,R,l,mid,rt*2))%mod;if (R>mid) ret=(ret+query(L,R,mid+1,r,rt*2+1))%mod;uppush(rt);return ret;}int main(){while(cin>>n>>m){build(1,n,1);while(m--){scanf("%d%d%d",&op,&a,&b);if (op==1){cin>>x;Mat m=pow(x);update(a,b,m,1,n,1);}else cout<<query(a,b,1,n,1)<<endl;}}return 0;}