@Asuna
2017-03-29T20:51:32.000000Z
字数 8329
阅读 790
题解
题意:一个队列,要求满足能单点加减和区间和查询。
题解:裸线段树,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);
else
return 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));
else
if (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);
else
return 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;
}