题意:W*W(W <= 500000)的矩阵,初始全是0,N(N <= 200000)个询问,单点加一个值,区间查询和。
分析:这题一看数据结构,二维线段树显然是不行的,不过还是可以水30分~
#include
#include
#include
using namespace std;
const int maxn=5500;
int c[maxn][maxn];
int lowbit(int x){
return x&-x;
}
void add(int x,int y,int k){
for(int i=x;ifor(int j=y;jint gsum(int x,int y){
int ans=0;
for(int i=x;i;i-=lowbit(i)){
for(int j=y;j;j-=lowbit(j)){
ans+=c[i][j];
}
}
return ans;
}
int query(int x1,int y1,int x2,int y2){
return gsum(x2,y2)+gsum(x1-1,y1-1)-gsum(x2,y1-1)-gsum(x1-1,y2);
}
int main(){
freopen("locust.in","r",stdin);
freopen("locust.out","w",stdout);
int w,n;
scanf("%d %d",&w,&n);
int flag,a,b,c,d;
while(n--){
scanf("%d",&flag);
if(flag==1){
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
}else{
scanf("%d%d%d%d",&a,&b,&c,&d);
printf("%d\n",query(a,b,c,d));
}
}
return 0;
}
当然,我们是无法满足于30分的,所以要写正解CDQ分治…
定义solve(l, r):寻找所有的修改操作∈(l, mid),计算他们对区间(mid+1,r)中查询操作的影响,然后不断分治。
对于这道题,我们在每次分治过程中,将区间内的操作按x轴排序,然后用一个一维的树状数组计算操作状态,然后查询操作直接计算即可。
#include
#include
#include
#include
#include
using namespace std;
const int maxn=200005;
struct work{
int x1,y1,x2,y2,op,k,num;
int ans;
}p[maxn],cc[maxn<<1];
int c[500005];
int w,n;
inline int in(){
int TEMP,EPX;
TEMP=0;EPX=getchar();
while(EPX<48||EPX>57)
EPX=getchar();
for(;EPX>47&&EPX<58;EPX=getchar())
TEMP=(TEMP<<3)+(TEMP<<1)+EPX-48;
return TEMP;
}
//树状数组 ***************
int lowbit(int x){
return x&(-x);
}
void add(int x,int k){
for(int i=x;i<=w;i+=lowbit(i)){
c[i]+=k;
}
}
int sum(int x){
int q=0;
for(int i=x;i;i-=lowbit(i)){
q+=c[i];
}
return q;
}
//************************
bool cmp(const work&a,const work&b){
if(a.x1==b.x1) return a.opreturn a.x1void solve(int l,int r){ //CDQ分治
if(l==r) return;
int mid=(l+r)>>1;
//printf("%d%d\n",l,r);
int cnt=0;
for(int i=l;i<=mid;i++){ // (l,mid)区间中修改操作
if(p[i].op==1)
cc[cnt++]=p[i];
}
for(int i=mid+1;i<=r;i++){ // (mid+1,r)区间中查询操作
if(p[i].op==2){
cc[cnt++]=p[i];
cc[cnt++]=p[i];
cc[cnt-2].x1--; //查询时需要用 x2的值减x1-1的值是区间(x1,x2) 的值
cc[cnt-1].x1=p[i].x2;
cc[cnt-1].op=3;
}
}
sort(cc,cc+cnt,cmp); //根据x轴排序
for(int i=0;iif(cc[i].op==1){
add(cc[i].y1,cc[i].k); //修改
}
else if(cc[i].op==2){
p[cc[i].num].ans -= sum(cc[i].y2)-sum(cc[i].y1-1); // x1-1前的修改值
}
else {
p[cc[i].num].ans += sum(cc[i].y2)-sum(cc[i].y1-1); // x2前的修改值
}
}
for(int i=0;iif(cc[i].op==1){
add(cc[i].y1,-cc[i].k); //清空c树状数组
}
}
solve(l,mid);
solve(mid+1,r); //分治
return ;
}
int MAIN(){
freopen("locust.in","r",stdin);
freopen("locust.out","w",stdout);
scanf("%d %d",&w,&n);
int flag,a,b,c,d;
for(int i=1;i<=n;i++){
//scanf("%d",&flag);
flag=in();
if(flag==1){
//scanf("%d%d%d",&a,&b,&c);
a=in();b=in();c=in();
p[i].op=1;
p[i].x1=a;
p[i].y1=b;
p[i].k=c;
}else{
//scanf("%d%d%d%d",&a,&b,&c,&d);
a=in();b=in();c=in();d=in();
p[i].op=2;
p[i].x1=min(a,c);
p[i].y1=min(b,d);
p[i].x2=max(a,c);
p[i].y2=max(b,d);
}
p[i].num=i;
}
solve(1,n);
for(int i=1;i<=n;i++){
if(p[i].op==2) printf("%d\n",p[i].ans);
}
return 0;
}
int main(){;}
int helenkeller=MAIN();
第一道CDQ分治就这样解决啦!!!
然后发现COGS上还有一道类似的题,叫 1752. [BOI2007]摩基亚Mokia
然后我以为这两个题是一样的,于是改了下数据范围就交了,然后,我就成功的MLE了…MLE了……..
然后我发现了我的程序的弊端,就是对于每次分治操作的时候,我另开了一个数组cc[],然而其实并不需要…
由于我实在懒得改…所以这里Orz Mikumikumi 学长…
#include
#include
#include
using namespace std;
int cmd,maxn=2000010,n,tot=0,ans[10010]={0},Bit[2000010]={0},H[160010];
class miku
{
public:
int x,y,k,s;
int q;
miku(){}
miku(int x1,int y1,int k1,int v1,int t)
{
x=x1,y=y1,k=k1,s=v1,q=t;
}
bool operator <(const miku a) const
{
return x2000010];
int lowbit(int x)
{
return x&-x;
}
int query(int x)
{
int re=0;
while(x>0){re+=Bit[x];x-=lowbit(x);}
return re;
}
void add(int x,int y)
{
while(x<=n){Bit[x]+=y;x+=lowbit(x);}
}
void solve(int l,int r)
{
if(l==r) return;
int mid=(l+r)>>1;
solve(l,mid);solve(mid+1,r);
sort(Q+l,Q+mid+1);sort(Q+mid+1,Q+r+1);
int L=l,inq=0;
for(int i=mid+1;i<=r;i++)
{
if(Q[i].k==2)
{
while(L<=mid&&Q[L].x<=Q[i].x)
{
if(Q[L].k==1)
{
add(Q[L].y,Q[L].s);
H[++inq]=L;
}
L++;
}
ans[Q[i].q]+=Q[i].s*query(Q[i].y);
}
//cout<
}
for(int i=1;i<=inq;i++)
add(Q[H[i]].y,-Q[H[i]].s);
}
void push(int x,int y,int k,int v,int t)
{
if(x>0&&y>0)
Q[++tot]=miku(x,y,k,v,t);
}
int main()
{
freopen("mokia.in","r",stdin);
freopen("mokia.out","w",stdout);
int x1,y1,x2,y2,v,tail=0;
while(scanf("%d",&cmd)&&cmd!=3)
{
if(cmd==0)
{
scanf("%d",&n);
}
if(cmd==1)
{
scanf("%d%d%d",&x1,&x2,&v);
push(x1,x2,1,v,0);
//printf("1");
}
if(cmd==2)
{
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
tail++;
push(x2,y2,2,1,tail);
push(x1-1,y1-1,2,1,tail);
push(x1-1,y2,2,-1,tail);
push(x2,y1-1,2,-1,tail);
//printf("2");
}
}
solve(1,tot);
for(int i=1;i<=tail;i++) printf("%d\n",ans[i]);
return 0;
}
然后还没完…我发现[IOI2001]移动电话 貌似和这两个题一样…这TM真的是IOI么这么水…然后我又有新的发现…这题数据范围简直小啊..这就可以直接用用二维树状数组搞了…而且貌似暴力也可以过…
我就不贴代码了…题你们也自己找~
HelenKeller
2016.7.7