@TryMyEdge
2017-02-11T14:31:08.000000Z
字数 9440
阅读 816
题解
A 敌兵布阵
题目大意:
有N(0<N<=50000)个营地,第i个营地中有ai(1<=ai<=50)个士兵。
有四种命令:
(1)Add i j,表示第i个营地增加j个人
(2)Sub i j,表示第i个营地减少j个人
(3)Query i j,表示询问第i到第j个营地的总人数
(4)End 表示当前数据结束
一组数据最多有40000条命令。
T组数据。
解题思路:
线段树裸题。
单点更新,维护区间和。
时间复杂度o(T*40000*log(N)),空间复杂度o(N)。
AC代码:
**#include<iostream>
#include<bits/stdc++.h>
#define pq priority_queue
#define Pi acos(-1.0)
using namespace std;
struct Node
{
int l,r,mid,sum;
}tre[200005];
int n,num[50005];
int build(int l,int r,int x)
{
tre[x].l=l;
tre[x].r=r;
tre[x].mid=(l+r)/2;
if(l==r)
return tre[x].sum=num[l];
else
return tre[x].sum=build(l,tre[x].mid,x*2)+build(tre[x].mid+1,r,x*2+1);
}
int ask(int l,int r,int x)
{
if(l==tre[x].l && r==tre[x].r)
return tre[x].sum;
else
{
if(l<=tre[x].mid && r>tre[x].mid)
return ask(l,tre[x].mid,x*2)+ask(tre[x].mid+1,r,x*2+1);
else
{
if(r<=tre[x].mid)
return ask(l,r,x*2);
else
return ask(l,r,x*2+1);
}
}
}
void add(int now,int nums,int x)
{
tre[x].sum+=nums;
if(tre[x].l!=tre[x].r)
{
if(now<=tre[x].mid)
add(now,nums,x*2);
else
add(now,nums,x*2+1);
}
}
int main()
{
int T,a,b;
char s[6];
cin>>T;
for(int t=1;t<=T;t++)
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",num+i);
build(1,n,1);
printf("Case %d:\n",t);
while(scanf("%s",s),s[0]!='E')
{
scanf("%d%d",&a,&b);
if(s[0]=='Q')
printf("%d\n",ask(a,b,1));
if(s[0]=='A')
add(a,b,1);
if(s[0]=='S')
add(a,-b,1);
}
}
return 0;
}**
B Color the ball
题目大意:
有N(N<=100000)个气球,进行N次涂色,每次涂色可以指定一段任意数目的连续的气球,问最后每个气球被涂过多少次颜色。
多组数据。
解题思路:
线段树维护被涂色的次数。
查询的时候把路径上的染色数都加起来。
时间复杂度o(N*log(N)),空间复杂度o(N)。
AC代码:
#include<iostream>
#include<bits/stdc++.h>
#define pq priority_queue
#define Pi acos(-1.0)
using namespace std;
struct Node
{
int l,r,mid,num;
}tre[400005];
int n,temp;
void build(int l,int r,int x)
{
tre[x].l=l;
tre[x].r=r;
tre[x].mid=(l+r)/2;
tre[x].num=0;
if(l!=r)
{
build(l,tre[x].mid,x*2);
build(tre[x].mid+1,r,x*2+1);
}
}
void ask(int now,int x)
{
temp+=tre[x].num;
if(tre[x].l!=tre[x].r)
{
if(now<=tre[x].mid)
ask(now,x*2);
else
ask(now,x*2+1);
}
}
void add(int l,int r,int x)
{
if(tre[x].l==l && r==tre[x].r)
tre[x].num++;
else
{
if(l<=tre[x].mid && r>tre[x].mid)
{
add(l,tre[x].mid,x*2);
add(tre[x].mid+1,r,x*2+1);
}
else
{
if(r<=tre[x].mid)
return add(l,r,x*2);
else
return add(l,r,x*2+1);
}
}
}
int main()
{
int a,b;
while(scanf("%d",&n),n)
{
build(1,n,1);
for(int i=0;i<n;i++)
{
scanf("%d%d",&a,&b);
add(a,b,1);
}
for(int i=1;i<=n;i++)
{
if(i-1)
printf(" ");
temp=0;
ask(i,1);
printf("%d",temp);
}
printf("\n");
}
return 0;
}
C Mayor's posters
题目大意:
有N(1<=N<=10000)张海报,按给出的顺序依次贴在一面墙上,海报的高度相同,第i张海报的左右端点为li和ri(1<=li<=ri<=10^7),问最终能看到几张不同的海报。
T组数据。
解题思路:
10^7*log(10^7)而且是多组(而且是poj23333),时间复杂度BOOM!
所以要将点坐标离散化。再用线段树维护当前的海报编号,最后扫一遍整颗线段树看一下哪些编号还在树上。
小细节:两个点之间的一段没有点的区间,也要离散化成为一个点。
时间复杂度o(T*N*log(N)),空间复杂度o(N)。
AC代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define pq priority_queue
#define Pi acos(-1.0)
using namespace std;
struct Node
{
int l,r,mid,color;
}tre[200005];
int n;
struct Post
{
int num,no;
}c[20005];
bool life[10005];
int col[40005];
bool cmp1(Post a,Post b)
{
if(a.num==b.num)
return a.no<b.no;
else
return a.num<b.num;
}
bool cmp2(Post a,Post b)
{
return a.no<b.no;
}
void build(int l,int r,int x)
{
tre[x].l=l;
tre[x].r=r;
tre[x].mid=(l+r)/2;
tre[x].color=0;
if(l!=r)
{
build(l,tre[x].mid,x*2);
build(tre[x].mid+1,r,x*2+1);
}
}
void ok(int l,int r,int color,int x)
{
if(tre[x].l==l && r==tre[x].r)
tre[x].color=color;
else
{
if(tre[x].color)
{
ok(tre[x].l,tre[x].mid,tre[x].color,x*2);
ok(tre[x].mid+1,tre[x].r,tre[x].color,x*2+1);
tre[x].color=0;
}
if(l<=tre[x].mid && r>tre[x].mid)
{
ok(l,tre[x].mid,color,x*2);
ok(tre[x].mid+1,r,color,x*2+1);
}
else
{
if(r<=tre[x].mid)
return ok(l,r,color,x*2);
else
return ok(l,r,color,x*2+1);
}
}
}
void rebuild(int x)
{
if(tre[x].color)
{
for(int i=tre[x].l;i<=tre[x].r;i++)
col[i]=tre[x].color;
}
else
{
if(tre[x].l==tre[x].r)
return;
rebuild(x*2);
rebuild(x*2+1);
}
}
int main()
{
int T,m,ans;
int cnt,gg;
cin>>T;
while(T--)
{
scanf("%d",&m);
n=0;
for(int i=0;i<m;i++)
{
scanf("%d%d",&(c[i*2].num),&(c[i*2+1].num));
c[i*2].no=i*2;
c[i*2+1].no=i*2+1;
}
sort(c,c+m*2,cmp1);
cnt=1;
gg=c[0].num;
c[0].num=1;
for(int i=1;i<2*m;i++)
{
cnt+=(c[i].num>gg);
cnt+=(c[i].num>gg+1);
gg=c[i].num;
c[i].num=cnt;
}
sort(c,c+m*2,cmp2);
build(1,cnt,1);
for(int i=0;i<m;i++)
{
ok(c[i*2].num,c[i*2+1].num,i+1,1);
}
memset(life,0,sizeof(life));
ans=0;
rebuild(1);
life[0]=1;
for(int i=1;i<=cnt;i++)
{
if(!life[col[i]])
{
ans++;
life[col[i]]=1;
}
}
printf("%d\n",ans);
}
return 0;
}
D Tunnel Warfare
题目大意:
有N(N<=50000)个村庄,M(M<=50000)个事件。
事件分为三种:
(1)D x,表示摧毁x号村庄
(2)Q x,表示询问x所在的连续的完好村庄段的长度
(3)R,表示修复当前最后被摧毁的那个村庄
多组数据。
解题思路:
用一个栈维护已经摧毁的村庄,可以知道每次要恢复哪个村庄。
线段树维护左起连续完好村庄段的长度和右起连续完好村庄段的长度。
询问的时候如果中间的连续完好村庄段包含询问的点,则返回这一段的长度,否则继续向下询问。
小细节:多组数据题目没说清楚挺坑的。。。还有似乎会出现重复摧毁已经摧毁过的村庄或者重复恢复当前完好的村庄的情况。
时间复杂度o(M*log(N)),空间复杂度o(N)。
AC代码:
#include<iostream>
#include<cstdio>
#define pq priority_queue
#define Pi acos(-1.0)
using namespace std;
struct Node
{
int l,r,mid;
int lans,rans;
}tre[200005];
int n,m;
bool life[50005];
void build(int l,int r,int x)
{
tre[x].l=l;
tre[x].r=r;
tre[x].mid=(l+r)/2;
tre[x].lans=tre[x].rans=r-l+1;
if(l!=r)
{
build(l,tre[x].mid,x*2);
build(tre[x].mid+1,r,x*2+1);
}
}
void change(int now,int flag,int x)
{
if(tre[x].l==tre[x].r)
tre[x].lans=tre[x].rans=flag;
else
{
if(now<=tre[x].mid)
change(now,flag,x*2);
else
change(now,flag,x*2+1);
if(tre[x*2].lans==tre[x*2].r-tre[x*2].l+1)
tre[x].lans=tre[x*2].lans+tre[x*2+1].lans;
else
tre[x].lans=tre[x*2].lans;
if(tre[x*2+1].rans==tre[x*2+1].r-tre[x*2+1].l+1)
tre[x].rans=tre[x*2+1].rans+tre[x*2].rans;
else
tre[x].rans=tre[x*2+1].rans;
}
}
int ask(int now,int x)
{
if(now<=tre[x].mid)
{
if(tre[x].mid-tre[x*2].rans<now)
return tre[x*2].rans+tre[x*2+1].lans;
else
return ask(now,x*2);
}
else
{
if(tre[x].mid+tre[x*2+1].lans>=now)
return tre[x*2].rans+tre[x*2+1].lans;
else
return ask(now,x*2+1);
}
}
int st[50005],cnt;
int main()
{
while(cin>>n>>m)
{
build(1,n,1);
char s[6];
int x;
cnt=0;
memset(life,0,sizeof(life));
while(m--)
{
scanf("%s",s);
if(s[0]=='D')
{
scanf("%d",&x);
st[cnt++]=x;
change(x,0,1);
life[x]=1;
}
if(s[0]=='R')
{
cnt--;
x=st[cnt];
change(x,1,1);
life[x]=0;
}
if(s[0]=='Q')
{
scanf("%d",&x);
if(life[x])
printf("0\n");
else
printf("%d\n",ask(x,1));
}
}
}
return 0;
}
E A Simple Problem with Integers
题目大意:
有N(1<=N<=10^5)个数,第i个数为Ai(-10^9<=Ai<=10^9),Q个(1<=Q<=10^5)操作。
操作分为两种:
(1)C a b c,表示第a个到第b个数全部加上c(-10^4<=c<=10^4)
(2)Q a b,表示询问第a个数到b个数的和
解题思路:
线段树维护区间和,用lazy标记来优化区间操作。
小细节:注意爆int的问题。
时间复杂度o(Q*log(N)),空间复杂度o(N)。
AC代码:
#include<iostream>
#include<cstdio>
#define pq priority_queue
#define Pi acos(-1.0)
using namespace std;
struct Node
{
int l,r,mid;
long long sum,lazy;
}tre[400005];
int n;
int num[100005];
long long build(int l,int r,int x)
{
tre[x].l=l;
tre[x].r=r;
tre[x].mid=(l+r)/2;
tre[x].lazy=0;
if(l!=r)
return tre[x].sum=build(l,tre[x].mid,x*2)+build(tre[x].mid+1,r,x*2+1);
else
return tre[x].sum=num[l];
}
void add(int l,int r,long long nums,int x)
{
tre[x].sum+=nums*(r-l+1);
if(tre[x].l==l && r==tre[x].r)
tre[x].lazy+=nums;
else
{
add(tre[x].l,tre[x].mid,tre[x].lazy,x*2);
add(tre[x].mid+1,tre[x].r,tre[x].lazy,x*2+1);
tre[x].lazy=0;
if(l<=tre[x].mid && r>tre[x].mid)
{
add(l,tre[x].mid,nums,x*2);
add(tre[x].mid+1,r,nums,x*2+1);
}
else
{
if(r<=tre[x].mid)
add(l,r,nums,x*2);
else
add(l,r,nums,x*2+1);
}
}
}
long long ask(int l,int r,int x)
{
if(l==tre[x].l && r==tre[x].r)
return tre[x].sum;
else
{
add(tre[x].l,tre[x].mid,tre[x].lazy,x*2);
add(tre[x].mid+1,tre[x].r,tre[x].lazy,x*2+1);
tre[x].lazy=0;
if(l<=tre[x].mid && r>tre[x].mid)
return ask(l,tre[x].mid,x*2)+ask(tre[x].mid+1,r,x*2+1);
else
{
if(r<=tre[x].mid)
return ask(l,r,x*2);
else
return ask(l,r,x*2+1);
}
}
}
int main()
{
int q,a,b;
int c;
char s[6];
cin>>n>>q;
for(int i=1;i<=n;i++)
scanf("%d",num+i);
build(1,n,1);
while(q--)
{
scanf("%s",s);
if(s[0]=='C')
{
scanf("%d%d%d",&a,&b,&c);
add(a,b,c,1);
}
else
{
scanf("%d%d",&a,&b);
printf("%lld\n",ask(a,b,1));
}
}
return 0;
}
F Sasha and Array
题目大意:
有N(1<=N<=10^5)个数,第i个数为ai(1<=ai<=10^9),Q个(1<=Q<=10^5)操作。
操作分为两种:
(1)1 l r x,表示第l个到第r个数全部加上x(1<=x<=10^9)
(2)2 l r,表示询问第l个数到r个数对应的f(ai)的和,f(ai)表示斐波那契的第ai项,f(1)=f(2)=1
输出取模10^9+7。
解题思路:
乍看之下题意的描述和E几乎一模一样,事实上最终的代码也很相似(强行
斐波那契函数可以由原始向量通过不断左乘转移矩阵x-1次来得到表示第x项的向量,由于原始向量是固定的,于是斐波那契函数的第x项可以用转移矩阵的x-1次方来表示。因为x可能非常大所以要到用矩阵快速幂。几个项斐波那契数的相加则可以用矩阵的相加来实现。于是定义完关于矩阵的一系列操作,我们就可以很容易的把E题的代码转化为F题的代码,其中sum和lazy都是矩阵类型的。
小细节:注意f(x)是用矩阵的x-1次方来表示的。注意处理好取模,爆int。
时间复杂度o(Q*log(N)*log(x)),空间复杂度o(N)。
AC代码:
#include<iostream>
#include<cstdio>
#define pq priority_queue
#define Pi acos(-1.0)
using namespace std;
#define MOD 1000000007
struct JZ
{
long long a,b,c,d;
friend JZ operator *(JZ x1,JZ x2)
{
JZ x3;
x3.a=(x1.a*x2.a+x1.b*x2.c)%MOD;
x3.b=(x1.a*x2.b+x1.b*x2.d)%MOD;
x3.c=(x1.c*x2.a+x1.d*x2.c)%MOD;
x3.d=(x1.c*x2.b+x1.d*x2.d)%MOD;
return x3;
}
friend JZ operator +(JZ x1,JZ x2)
{
JZ x3;
x3.a=(x1.a+x2.a)%MOD;
x3.b=(x1.b+x2.b)%MOD;
x3.c=(x1.c+x2.c)%MOD;
x3.d=(x1.d+x2.d)%MOD;
return x3;
}
}A,E;
JZ Pows(int x)
{
JZ X=E,Y=A;
while(x)
{
if(x%2)
X=Y*X;
Y=Y*Y;
x/=2;
}
return X;
}
struct Node
{
int l,r,mid;
JZ sum,lazy;
}tre[400005];
int n;
int num[100005];
JZ build(int l,int r,int x)
{
tre[x].l=l;
tre[x].r=r;
tre[x].mid=(l+r)/2;
tre[x].lazy=E;
if(l!=r)
return tre[x].sum=build(l,tre[x].mid,x*2)+build(tre[x].mid+1,r,x*2+1);
else
return tre[x].sum=Pows(num[l]-1);
}
void add(int l,int r,JZ nums,int x)
{
if(tre[x].l==l && r==tre[x].r)
{
tre[x].sum=nums*tre[x].sum;
tre[x].lazy=nums*tre[x].lazy;
}
else
{
add(tre[x].l,tre[x].mid,tre[x].lazy,x*2);
add(tre[x].mid+1,tre[x].r,tre[x].lazy,x*2+1);
tre[x].lazy=E;
if(l<=tre[x].mid && r>tre[x].mid)
{
add(l,tre[x].mid,nums,x*2);
add(tre[x].mid+1,r,nums,x*2+1);
}
else
{
if(r<=tre[x].mid)
add(l,r,nums,x*2);
else
add(l,r,nums,x*2+1);
}
tre[x].sum=tre[x*2].sum+tre[x*2+1].sum;
}
}
JZ ask(int l,int r,int x)
{
if(l==tre[x].l && r==tre[x].r)
return tre[x].sum;
else
{
add(tre[x].l,tre[x].mid,tre[x].lazy,x*2);
add(tre[x].mid+1,tre[x].r,tre[x].lazy,x*2+1);
tre[x].lazy=E;
if(l<=tre[x].mid && r>tre[x].mid)
return ask(l,tre[x].mid,x*2)+ask(tre[x].mid+1,r,x*2+1);
else
{
if(r<=tre[x].mid)
return ask(l,r,x*2);
else
return ask(l,r,x*2+1);
}
}
}
int main()
{
E.b=E.c=A.d=0;
E.a=E.d=A.a=A.b=A.c=1;
int q,a,b,c,s;
cin>>n>>q;
for(int i=1;i<=n;i++)
scanf("%d",num+i);
build(1,n,1);
while(q--)
{
scanf("%d",&s);
if(s==1)
{
scanf("%d%d%d",&a,&b,&c);
add(a,b,Pows(c),1);
}
else
{
scanf("%d%d",&a,&b);
printf("%lld\n",(ask(a,b,1)).a);
}
}
return 0;
}