【题解】L 国的战斗续之多路出击 [P2129]

【题解】L 国的战斗续之多路出击 [P2129]

传送门: \(L\) 国的战斗续之多路出击 \([P2129]\)

【题目描述】

给出 \(n\) 个坐标,\(m\) 个指令,指令处理顺序应是从后往前

求最终的 \(n\) 个坐标。

【输入】

第一行两个整数 \(n,m\)

接下来 \(n\) 行,每行两个整数 \(x_i,y_i\) 表示第 \(i\) 个坐标。

然后 \(m\) 行,每行首先一个字符 \(C\)

\((1).\)\(C\)\(\text{'m'}\),则紧跟两个整数 \(p,q\),要求把所有坐标 \((x_i,y_i)\) 变为 \((x_i+p,y_i+q)\)

\((2).\)\(C\)\(\text{'x'}\),则把所有坐标从 \((xi,yi)\) 变为 \((-xi,yi)\)

\((3).\)\(C\)\(\text{'y'}\),则把所有坐标从 \((xi,yi)\) 变为 \((xi,-yi)\)

【输出】

扫描二维码关注公众号,回复: 7340710 查看本文章

【样例】

样例输入:
3 3
0 0
4 -3
6 7
x
m -1 2
y

样例输出:
1 2
-3 5
-5 -5

【数据范围】

\(30 \%:\) \(1 \leqslant n,m \leqslant 1000\)

\(100 \%:\) \(1 \leqslant n,m \leqslant 5*10^5\)


【分析】

大佬们都写的是矩阵乘法或者模拟,向我这种不会矩阵和膜您的蒟蒻就只能写线段树了 \(\text{QAQ}\) (好像是第一个用这种奇葩方法的?)。

区间加,区间乘的模板,但因为是单调查询,而且没有取模这种鬼畜操作,比【模板】线段树 \(2\)维护序列 要简单得多。

维护一个乘法标记 \(mul\) 和加法标记 \(add\),区间加时就直接更新 \(add\),区间乘 \(v\) 就先让 \(mul\)\(add\) 都乘以 \(v\)

下传标记时先传 \(mul\) 再传 \(add\)

其他的就直接照着题目模拟就可以了。

坑点:\(m\) 个指令要倒着处理。

【Code】

#include<cstdio>
#define Re register int
#define pl (p<<1)//左儿子
#define pr (p<<1|1)//右儿子
#define mid (L+R>>1)
#define pa tr[p].add//加法标记
#define pm tr[p].mul//乘法标记
const int N=5e5+5;
int n,x,y,T,a[N],b[N];
inline void in(Re &x){
    Re f=0;x=0;char c=getchar();
    while(c<'0'||c>'9')f|=c=='-',c=getchar();
    while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
    x=f?-x:x;
}
struct Segment_Tree{
    struct QAQ{int l,r,ans,add,mul;}tr[N<<2];
    inline void updata_add(Re p,Re v){
        if(tr[p].l==tr[p].r)tr[p].ans+=v;//只需要更新叶子节点
        pa+=v;//但标记必须下传
    }
    inline void updata_mul(Re p,Re v){
        if(tr[p].l==tr[p].r)tr[p].ans*=v;//同上
        pm*=v,pa*=v;
    }
    inline void pushdown(Re p){//先下传乘法标记,再下传加法标记
        if(pm!=1)updata_mul(pl,pm),updata_mul(pr,pm),pm=1;//这里乘法标记也要初始化为1
        if(pa)updata_add(pl,pa),updata_add(pr,pa),pa=0;
    }
    inline void build(Re p,Re L,Re R){//初始化建树
        tr[p].l=L,tr[p].r=R,pm=1;//乘标记要初始化为1
        if(L==R){tr[p].ans=a[L];return;}
        build(pl,L,mid),build(pr,mid+1,R);
    }
    inline void change_add(Re p,Re l,Re r,Re v){//区间加
        Re L=tr[p].l,R=tr[p].r;
        if(l<=L&&R<=r){updata_add(p,v);return;}
        pushdown(p);
        if(l<=mid)change_add(pl,l,r,v);
        if(r>mid)change_add(pr,l,r,v);
    }
    inline void change_mul(Re p,Re l,Re r,Re v){//区间乘
        Re L=tr[p].l,R=tr[p].r;
        if(l<=L&&R<=r){updata_mul(p,v);return;}
        pushdown(p);
        if(l<=mid)change_mul(pl,l,r,v);
        if(r>mid)change_mul(pr,l,r,v);
    }
    inline int ask(Re p,Re w){//单点查询
        Re L=tr[p].l,R=tr[p].r;
        if(L==R)return tr[p].ans;
        pushdown(p);
        if(w<=mid)return ask(pl,w);
        else return ask(pr,w);
    }
}T1,T2;//T1:x坐标。T2:y坐标。
inline void sakura(Re T){
    Re x,y;char op;
    if(!T)return;
    scanf(" %c",&op);
    if(op=='m')in(x),in(y);
    sakura(T-1);
    if(op=='x')T1.change_mul(1,1,n,-1);//(x,y) -> (-x,y)
    else if(op=='y')T2.change_mul(1,1,n,-1);//(x,y) ->(x,-y)
    else T1.change_add(1,1,n,x),T2.change_add(1,1,n,y);//(x,y) -> (x+p,y+q)
}
int main(){
    // freopen("123.txt","r",stdin);
    in(n),in(T);
    for(Re i=1;i<=n;++i)in(a[i]),in(b[i]);
    T1.build(1,1,n);//第一棵树
    for(Re i=1;i<=n;++i)a[i]=b[i];
    T2.build(1,1,n);//第二课树
    sakura(T);
    for(Re i=1;i<=n;++i)printf("%d %d\n",T1.ask(1,i),T2.ask(1,i));
}

猜你喜欢

转载自www.cnblogs.com/Xing-Ling/p/11571500.html