bzoj 1062: [NOI2008]糖果雨

高手

 

/**************************************************************
    Problem: 1062
    User: lxy8584099
    Language: C++
    Result: Accepted
    Time:4248 ms
    Memory:71916 kb
****************************************************************/
 
/*
    所有的事件按照输入顺序依次发生
    搞了半天不需要可持续。。。 
    首先看题解
    对于每一个云 有(出现时间,范围,运动方向) 
    因为运动是往返的 存在周期性
    把往右移动的云看做+len位置后 向左移动 
    所以周期为 2*len 
    所以对于所有的 t和 t%(2*len) 效果是一样的 
    而且询问和add cut都是按时间来的 
    所以我们可以将一个云表示为 
        (云的左端运动到左边界的时间 ,云的长度)
    放进二位坐标系
    对于add
        可以得到一个新的云的坐标为 
        ( (Ti-Li*Di)%(2*len) ,Ri-Li )
        记录下改颜色对应的坐标 方便删除 
        放进二位的树状数组 
    对于cut
        时间都没作用 
        因为任何时候 云的颜色互异 
        直接在二维树状数组里删除对应颜色的坐标
    对于ask (t,l,r)
        因为云是往返运动 
        所以 (t,l,r)的覆盖面积是关于x=len对称的 
        先考虑x<=len的情况
        考虑重合 x较大的时候和其重合的概率较小 
        因为此时左端点就很大 
        可能经过一段时间后左端点任然在r的右边 
        就一定不会有重合
        因此 要满足 x-t<=r => x<=t+r 
        即左端点左移t和单位要在r的左边 
        同样的 x较小的时候重合概率也很小
        因为此时左端点碰到左边也右移 
        时间足够长就可能移到r的右边
        所以要满足 t-x<=r => x>=t-r 
        综上 t-r<=x<=t+r 
        左端点满足此条件是可能重合 
        如果该云长度过小也可能不重合 
        已经满足左端点<=r 如果右端点>=l 则一定有重合 
        而 该云从开始运动 t 的时间 
        距离左边界 |x-t|
        所以 length>=l-|x-t| 才能保右端点>=l  
        画出来的图像很奇葩 可以补全为一个平行四边形
        虽然面积变大了 
        但是点指挥出现在合法的面积内 不影响答案 
        对于存在( y=-x+k ) 的平行四边形 
        我们把坐标变为 (x,x+y)
        对于存在( y=x+k ) 的平行四边形 
        我们把坐标变为 (x,y-x+len*2) 
            +是为了避免出现负数
        然后维护两种个扭曲的坐标系
        所以树状数组修改的时候要修改两种 Qwq 
    注意 
    树状数组没法保存下标为0的信息  下标都后移一位
    r==len 一条直线上的点会被重复计算 判重 
*/
#include<bits/stdc++.h> 
#define lowbit(x) ((x)&(-x))
using namespace std;
const int N=2005,M=1000010;
int n,len,len2,len4; // 表示 2倍的len  4倍的len 
int s[2][N][N<<1],x[M],y[M];
void add(int k,int a,int b,int c)
{
    a++;b++;
    for(int i=a;i<=len2;i+=lowbit(i))
    for(int j=b;j<=len4;j+=lowbit(j)) 
        s[k][i][j]+=c;
    // 因为第二维有+len2 所以要循环到len4 
}
int sum(int k,int a,int b)
{
    a++;b++; int res=0;
    if(a<=0||b<=0) return 0; // 判断合法  
    if(a>len2) a=len2;
    if(b>len4) b=len4; // 约数在点集范围内 
    for(int i=a;i;i-=lowbit(i))
    for(int j=b;j;j-=lowbit(j))
        res+=s[k][i][j];
    return res;
}
int calc(int k,int x1,int y1,int x2,int y2)
{
    return sum(k,x2,y2)-sum(k,x2,y1-1)
            -sum(k,x1-1,y2)+sum(k,x1-1,y1-1);
}
int main()
{
    scanf("%d%d",&n,&len);;
    len2=(len<<1);len4=(len2<<1);
    while(n--)
    {
        int op,t,c,l,r,d;
        scanf("%d",&op);
        if(op==1)
        {
            scanf("%d%d%d%d%d",&t,&c,&l,&r,&d);
            x[c]=((t-l*d)%len2+len2)%len2;
            y[c]=r-l;
            add(0,x[c],x[c]+y[c],1);
            add(1,x[c],-x[c]+y[c]+len2,1);
        }
        else if(op==2)
        {
            scanf("%d%d%d",&t,&l,&r); t%=len2;
            int res=0,flag=(r==len); // 特殊情况 
            res+=calc(0,t,l+t,t+r,len4);
            res+=calc(0,0,l+t-len2,t+r-len2-flag,len4);
            res+=calc(1,t-r+len2+flag,l-t,len2,len4);
            res+=calc(1,t-r,l-t+len2,t-1,len4);
            printf("%d\n",res);
        }
        else
        {
            scanf("%d%d",&t,&c); // 时间递增 t没用。。。 
            add(0,x[c],x[c]+y[c],-1);
            add(1,x[c],-x[c]+y[c]+len2,-1);
        }
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/lxy8584099/p/10310818.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值