http://poj.org/problem?id=2991
吊车由n条不同长度的线段组成,每条线段首尾相接。初始状态:每条线段垂直与x轴。每次操作改变第s条和(s+1)条的逆时针角度为a,询问每次操作后第n段末尾的坐标。
将每条线段都当成向量,实际上每次询问的结果是向量和;
每次改变第s段和第(s+1)段的相对角度,实际上是改变了从第(s+1)段至第n段的各节点角度,则可以通过线段树进行区间更新,(s+i+1,s+i+2)之间的角度还是不改变
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<queue>
#include <time.h>
#include<math.h>
#include<string>
#include<vector>
#include<utility>
using namespace std;
const int ST_SIZE=(1<<15)-1;
const double PI=acos(-1.0);
const int MAX_N=10010;
///输入
int N,C;
int L[MAX_N];
int S[MAX_N],A[MAX_N];
double vx[ST_SIZE],vy[ST_SIZE];///记录各节点的向量
double ang[ST_SIZE];///记录各节点的角度
double prv[MAX_N];///为了查询角度变化而保存的当前角度的数组
void init(int k,int l,int r)///初始化,建树,k结点编号,l,r表示当前节点对应的是[l,r)区间
{
ang[k]=vx[k]=0.0;
if(r-l==1)
{
///叶子
vy[k]=L[l];
}
else
{
///非叶子
int chl=k*2+1,chr=k*2+2;
init(chl,l,(l+r)/2);
init(chr,(l+r)/2,r);
vy[k]=vy[chl]+vy[chr];
}
}
///v是节点编号,l,r表示当前节点对应的是[l,r)区间
void change(int s,double a,int v,int l,int r)
{
if(s<=l)
return;
else if(s<r)
{
int chl=2*v+1,chr=2*v+2;
int m=(l+r)/2;
change(s,a,chl,l,m);
change(s,a,chr,m,r);
if(s<=m)///对比s小的没有影响
ang[v]+=a;
double s=sin(ang[v]),c=cos(ang[v]);
vx[v]=vx[chl]+(c*vx[chr]-s*vy[chr]);
vy[v]=vy[chl]+(c*vy[chr]+s*vx[chr]);
}
}
void solve()
{
init(0,0,N);///初始化
for(int i=0; i<N; i++)
prv[i]=PI;///i和(i+1)之间的角度
for(int i=0; i<C; i++)
{
scanf("%d%d",&S[i],&A[i]);
int s=S[i];
double a=A[i]/360.0*2*PI;///将s和s+i之间的角度转为a(变为弧度制)
change(s,a-prv[s],0,0,N);///a-prv[s],s+i需要旋转的角度
prv[s]=a;
printf("%.2lf %.2lf\n",vx[0],vy[0]);
}
printf("\n");
}
int main()
{
while(~scanf("%d%d",&N,&C))
{
for(int i=0; i<N; i++)
scanf("%d",&L[i]);
solve();
}
return 0;
}
http://poj.org/problem?id=3468
给定一个数列A1A2.....An以及Q个操作
1)给出l,r,x,对Al,Al+1,...,Ar同时加上x
2)给出l,r,求Al,Al+1,...,Ar
线段树
#include<stdio.h>
#include<algorithm>
#include<string>
#include<string.h>
#include<math.h>
using namespace std;
typedef long long ll;
const int DAT_SIZE=(1<<18)-1;
const int maxn=100010;
int N,Q;
int A;
char T[2];
int L,R,X;
///A,L,R都是以0为下标起点的
///线段树
ll data[DAT_SIZE];///对应区间全部和(全部都要加上某一个值)
ll datb[DAT_SIZE];///对应区间部分和(部分都要加上某一个值)
///对区间[a,b)同时加上x,k是节点编号,对应区间是[l,r)
void add(int a,int b,int x,int k,int l,int r)
{
if(a<=l&&r<=b)
{
data[k]+=x;///k所在的区间都要加上x
}
else if(b>l&&a<r)
{
datb[k]+=(min(b,r)-max(a,l))*x;///部分和
///向下找到一个区间内全部都加上x,否则那个区间就加上部分和
add(a,b,x,2*k+1,l,(l+r)/2);
add(a,b,x,2*k+2,(l+r)/2,r);
}
}
///计算区间[a,b)的和,k是节点编号,对应区间是[l,r)
ll sum(int a,int b,int k,int l,int r)
{
if(b<=l||r<=a)
{
return 0;
}
else if(a<=l&&r<=b)
{
return data[k]*(r-l)+datb[k];///全部和加上部分和
}
else
{
ll res=(min(b,r)-max(a,l))*data[k];///部分全部和
///向下找自己的部分和要加的部分
res+=sum(a,b,2*k+1,l,(r+l)/2);
res+=sum(a,b,2*k+2,(r+l)/2,r);
return res;
}
}
void solve()
{
for(int i=0;i<N;i++)
{
scanf("%d",&A);
add(i,i+1,A,0,0,N);
}
for(int i=0;i<Q;i++)
{
scanf("%s%d%d",T,&L,&R);
L-=1,R-=1;
if(T[0]=='C')
{
scanf("%d",&X);
add(L,R+1,X,0,0,N);
}
else
{
printf("%lld\n",sum(L,R+1,0,0,N));
}
}
}
int main()
{
memset(data,0,sizeof(data));
memset(datb,0,sizeof(datb));
scanf("%d%d",&N,&Q);
solve();
return 0;
}
树状数组
#include<stdio.h>
#include<iostream>
#include<string>
#include<string.h>
#include<algorithm>
#include<iostream>
using namespace std;
const int maxn=100010;
typedef long long ll;
int N,Q;
int A;
char T[2];
int L,R,X;
///A,L,R都是以1为下标的
///BIT树状数组
ll bit0[maxn],bit1[maxn];
ll sum(ll *b,int i)///求区间[1,i]的值
{
ll s=0;
while(i>0)
{
s+=b[i];
i-=i&-i;///i-=i&-i---->i=i&(i-1)(减去二进制非0位对应的幂)
}
return s;
}
void add(ll *b,int i,int x)///将i位上加上x
{
while(i<=N)
{
b[i]+=x;
i+=(i&-i);///(加上二进制非0位对应的幂)
}
}
void solve()
{
scanf("%d%d",&N,&Q);
for(int i=1;i<=N;i++)
{
scanf("%d",&A);
add(bit0,i,A);
}
for(int i=0;i<Q;i++)
{
scanf("%s%d%d",T,&L,&R);
if(T[0]=='C')
{
scanf("%d",&X);
add(bit0,L,-X*(L-1));
add(bit1,L,X);
add(bit0,R+1,X*R);
add(bit1,R+1,-X);/**包含[L,R]区间的bit0=-X*(L-1)+X*R=(R-L+1)*X,bit1=X+(-X)是区间要加值的总和
部分包含,如[L,i],i<R,bit0=-X*(L-1),bit1=X-->要加的值为-X*(L-1)+X*i=(i-L+1)*X是区间要加值的总和**/
}
else
{
ll res=0;
res+=sum(bit0,R)+sum(bit1,R)*R;
res-=sum(bit0,L-1)+sum(bit1,L-1)*(L-1);
printf("%lld\n",res);
}
}
}
int main()
{
solve();
return 0;
}
http://poj.org/problem?id=2104
给定一个数列a1,a2,...,an和m个三元组表示的查询.对于每个查询(i,j,k),输出ai,ai+1,...,aj的升序排列中的第k个数
分桶法和平方分割
如果桶完全包含在区间内,则查询桶的最小值
如果元素所在的桶不完全被区间包含,则逐个检查最小值
#include<stdio.h>
#include<iostream>
#include<string>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<vector>
using namespace std;
const int maxn=100010;
typedef long long ll;
const int B=1000;///桶的大小
int N,M;
int A[maxn];
int L,R,K;
///L,R是以0为下标起点的
vector<int>bucket[maxn/B];///每个桶排序后的结果
int num[maxn];///对A排序之后的结果;
void solve()
{
scanf("%d%d",&N,&M);
for(int i=0; i<N; i++)
{
scanf("%d",&A[i]);
bucket[i/B].push_back(A[i]);
num[i]=A[i];
}
sort(num,num+N);
///虽然每B个一组剩下的部分所在的桶没有排序,但是不会产生问题
for(int i=0; i<N/B; i++)
sort(bucket[i].begin(),bucket[i].end());
for(int i=0; i<M; i++)
{
///求[L,R+1)区间的第K个数
scanf("%d%d%d",&L,&R,&K);
L=L-1;
int lb=-1,ub=N-1;
while(ub-lb>1)
{
int mid=(lb+ub)/2;
int x=num[mid];
int tl=L,tr=R,c=0;
///区间两端多出的部分
while(tl<tr&&tl%B!=0)
if(A[tl++]<=x)
c++;
while(tl<tr&&tr%B!=0)
if(A[--tr]<=x)
c++;
///对每一个桶进行计算(二分搜索)
while(tl<tr)
{
int b=tl/B;
c+=upper_bound(bucket[b].begin(),bucket[b].end(),x)-bucket[b].begin();
tl+=B;
}
if(c>=K)
ub=mid;
else
lb=mid;
}
printf("%d\n",num[ub]);
}
}
int main()
{
solve();
return 0;
}
线段树
把数列用线段树维护起来,线段树的每个节点都保存了对应区间排好序的结果
如果所给区间和当前节点的区间完全没有交集,那么返回0
如果所给的区间完全包含了当前节点对应的区间,那么使用二分搜索法对该节点上保存的数组进行查找
否则对两个儿子递归进行计算求和
#include<stdio.h>
#include<iostream>
#include<string>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<vector>
using namespace std;
const int maxn=100010;
const int ST_SIZE=(1<<18)-1;
int N,M;
int A[maxn];
int L,R,K;
///L,R都是以0为下标起点的
int num[maxn];
vector<int>dat[ST_SIZE];
void init(int k,int l,int r)
{
if(r-l==1)
dat[k].push_back(A[l]);
else
{
int chl=2*k+1,chr=2*k+2;
init(chl,l,(l+r)/2);
init(chr,(l+r)/2,r);
dat[k].resize(r-l);///设置dat[k]中可以储存数的数量大小
///利用STL的merge函数把两个儿子的数列合并(默认合并为升序)
merge(dat[chl].begin(),dat[chl].end(),dat[chr].begin(),dat[chr].end(),dat[k].begin());
}
}
///计算区间[i,j)中不超过x的个数
///k是节点编号与区间[l,r)对应
int query(int i,int j,int x,int k,int l,int r)
{
if(j<=l||r<=i)return 0;///完全不相交
else if(i<=l&&r<=j)
{
///完全包含
return upper_bound(dat[k].begin(),dat[k].end(),x)-dat[k].begin();
}
else
{
///对儿子递归地计算
int lc=query(i,j,x,2*k+1,l,(l+r)/2);
int rc=query(i,j,x,2*k+2,(l+r)/2,r);
return lc+rc;
}
}
void solve()
{
scanf("%d%d",&N,&M);
for(int i=0;i<N;i++)
{
scanf("%d",&A[i]);
num[i]=A[i];
}
sort(num,num+N);
init(0,0,N);
for(int i=0;i<M;i++)
{
scanf("%d%d%d",&L,&R,&K);
L-=1;
int lb=-1,ub=N-1;
while(ub-lb>1)
{
int mid=(lb+ub)/2;
int c=query(L,R,num[mid],0,0,N);
if(c>=K)ub=mid;
else lb=mid;
}
printf("%d\n",num[ub]);
}
}
int main()
{
solve();
return 0;
}