19 南京icpc网络赛 The beautiful values of the palace//主席树 || 树状数组+二维前缀

题意:

给如图类似n*n矩阵(当前图n=5),给m个(x,y),使得除了m个点,其他点都变成0。

给q个询问,每次给一个矩阵,求矩阵里数字之和。

(题解)思路1: //离线

把查询的点拆成4个前缀查询。

把所有update和query的点都sort一下,离线的更新和查询树状数组。

#include<bits/stdc++.h>
using namespace std;
#define FI first
#define SE second
#define LL long long
#define MP make_pair
#define PII pair<int,int>
#define SZ(a) (int)a.size()
const double pai = acos(-1);
const LL mod = 1e9+7;
const int MX = 1e6+5;
int n,m;
vector<LL>v,vv;
int countxy(int x,int y){
    int cir=min(min(min(x-1,n-x),y-1),n-y);
    LL now;
    if(x==n-cir){
        int yy=n-cir;
        LL st=v[cir];
        now=st+abs(yy-y);
    }
    else if(y==cir+1){
        int xx=n-cir;
        LL st=v[cir]+vv[cir];
        now=st+abs(xx-x);
    }
    else if(x==cir+1){
        int yy=cir+1;
        LL st=v[cir]+vv[cir]*2LL;
        now=st+abs(yy-y);
    }
    else {
        int xx=cir+1;
        LL st=v[cir]+vv[cir]*3LL;
        now=st+abs(xx-x);
    }
    int re=0;
    while(now)re+=(now%10),now/=10;
    return re;
}
LL bit[MX];
void update(int pos,LL cc,int nn){
    while(pos<=nn) bit[pos]+=cc,pos=pos+(pos&-pos);
}
LL query(int pos){
    LL re=0;
    while(pos>0)re+=bit[pos],pos=pos-(pos&-pos);
    return re;
}
struct no{
    int x,y,fun,e;//1:加点,2:询问+ ,3:询问-
};
bool operator<(const no &x,const no &y) {
    if(x.x==y.x) {
        if(x.y==y.y) return x.fun<y.fun; //先加点,再询问
        return x.y<y.y;
    }
    return x.x<y.x;
}
vector<no>po;
LL ans[MX];
void Main(int avg){
    int q;
    cin>>n>>m>>q;
    v.clear();vv.clear();po.clear();
    memset(bit,0,sizeof(bit));memset(ans,0,sizeof(ans));
    v.push_back(1);
    for(LL i=n-1;i>=0;i-=2){
        v.push_back(i*4LL+v[SZ(v)-1]);vv.push_back(i);
    }
    vv.push_back(0);
    for(int i=1;i<=m;i++){
        int ssx,ssy;scanf("%d%d",&ssx,&ssy);po.push_back(no{ssx,ssy,1,countxy(ssx,ssy)});
    }
    for(int i=1;i<=q;i++){
        int ssx1,ssy1,ssx2,ssy2;scanf("%d%d%d%d",&ssx1,&ssy1,&ssx2,&ssy2);
        po.push_back(no{ssx1-1,ssy1-1,2,i});
        po.push_back(no{ssx2,ssy2,2,i});
        po.push_back(no{ssx1-1,ssy2,3,i});
        po.push_back(no{ssx2,ssy1-1,3,i});
    }
    sort(po.begin(),po.end());
    for(auto i:po){
        if(i.fun==1){
            update(i.y,(LL)i.e,1e6);
        }
        if(i.fun==2){
            ans[i.e]+=query(i.y);
        }
        if(i.fun==3){
            ans[i.e]-=query(i.y);
        }
    }
    for(int i=1;i<=q;i++)cout<<ans[i]<<'\n';
}
int main(){
    int cas;cin>>cas;for(int i=1;i<=cas;i++)Main(i);
    return 0;
}

(我的沙雕)思路2:  //支持离线,但是m不能到1e6级别(爆内存

可以分类讨论计算出m个点的值。离散化y。m个点从x小的到大的不断加入主席树。(每次加的不是1,是数位和)。

询问的时候,二分确定root,二分确定y的范围。

/*   (o O o)
 *   Author : Rshs
 *   Data : 2019-09-01-13.56
 */
#include<bits/stdc++.h>
using namespace std;
#define FI first
#define SE second
#define LL long long
#define MP make_pair
#define PII pair<int,int>
#define SZ(a) (int)a.size()
const LL mod = 1e9+7;
const int MX = 1e6+5;
const int max_n =1e5+5,logn=21;
int root[max_n*logn],ls[max_n*logn],rs[max_n*logn]; //下标为i的树他的左右子树的对应sum的下标
LL sum[max_n*logn];//下标为i的区间(上面的区间对应进来)的数字数量
int cnt;//树的数量
int idd[max_n];

struct no{
    int x,y;
}po[max_n];
bool operator<(const no &nn1,const no &nn2){
    return nn1.x<nn2.x;
}
vector<int>lsy,lsx;

int n,m;
vector<LL>v,vv;
int countxy(int x,int y){
    int cir=min(min(min(x-1,n-x),y-1),n-y);
    LL now;
    if(x==n-cir){
        int yy=n-cir;
        LL st=v[cir];
        now=st+abs(yy-y);
    }
    else if(y==cir+1){
        int xx=n-cir;
        LL st=v[cir]+vv[cir];
        now=st+abs(xx-x);
    }
    else if(x==cir+1){
        int yy=cir+1;
        LL st=v[cir]+vv[cir]*2LL;
        now=st+abs(yy-y);
    }
    else {
        int xx=cir+1;
        LL st=v[cir]+vv[cir]*3LL;
        now=st+abs(xx-x);
    }
    int re=0;
    while(now)re+=(now%10),now/=10;
    return re;
}
void update(int l,int r,int &now,int last,int pos,LL add){
    now=++cnt;//对应新开的区域
    ls[now]=ls[last];
    rs[now]=rs[last];
    sum[now]=sum[last]+add;
    if(l==r) return;
    int mid=(l+r)>>1;
    if(pos<=mid) update(l,mid,ls[now],ls[last],pos,add);
    else update(mid+1,r,rs[now],rs[last],pos,add);
}
LL Query(int L,int R,int l,int r,int t1,int t2){ //[L,R]里面元素个数
    if(l>=L&&r<=R)
        return 1LL*sum[t2]-sum[t1];
    int mid=(l+r)>>1;
    LL re=0;
    if(mid>=L) re+=Query(L,R,l,mid,ls[t1],ls[t2]);
    if(mid<R) re+=Query(L,R,mid+1,r,rs[t1],rs[t2]);
    return re;
}
void Main(){
    int q;
    cin>>n>>m>>q;
    cnt=0;lsx.clear(),lsy.clear();
    v.clear();vv.clear();
    v.push_back(1);
    for(LL i=n-1;i>=0;i-=2){
        v.push_back(i*4LL+v[SZ(v)-1]);vv.push_back(i);
    }
    vv.push_back(0);
    for(int i=1;i<=m;i++){
        scanf("%d%d",&po[i].x,&po[i].y);
        lsy.push_back(po[i].y);lsx.push_back(po[i].x);
    }
    sort(po+1,po+1+m);
    sort(lsy.begin(),lsy.end());sort(lsx.begin(),lsx.end());
    lsy.erase(unique(lsy.begin(),lsy.end()),lsy.end());
    int sz=lsy.size();
    for(int i=1;i<=m;i++){
        int nowy=lower_bound(lsy.begin(),lsy.end(),po[i].y)-lsy.begin()+1;
        update(1,sz,root[i],root[i-1],nowy,(LL)countxy(po[i].x,po[i].y));
    }
    while(q--){
        int x1,x2,y1,y2;scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
        int lx=lower_bound(lsx.begin(),lsx.end(),x1)-lsx.begin()+1;
        int rx=upper_bound(lsx.begin(),lsx.end(),x2)-lsx.begin();
        if(lx>rx) {cout<<0<<'\n';continue;}
        int ly=lower_bound(lsy.begin(),lsy.end(),y1)-lsy.begin()+1;
        int ry=upper_bound(lsy.begin(),lsy.end(),y2)-lsy.begin();
        if(ly>ry)  {cout<<0<<'\n';continue;}
        LL gg=Query(ly,ry,1,sz,root[lx-1],root[rx]);
        cout<<gg<<'\n';
    }
}
int main(){
    int ca;cin>>ca;for(int i=1;i<=ca;i++)Main();
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值