描述
有N张卡片,编号从0到n-1, 刚开始从0到n-1按顺序排好。
现有一个操作, 对于p、 l,表示从第p张卡片之后的l张卡片拿到 最前面。
例如n=7的时候, 刚开始卡片序列为0 1 2 3 4 5 6
对于操作p=2 l=3执行一次之后序列变为
2 3 4 0 1 5 6
求出所有操作之后, 奇数位上编号的和
输入
第一行两个整数 N、 M,表示有 N 张卡片,接下来 M 个操作。
接下来 M 行, 每行有三个整数 p、 l、 r, 表示重复 r 次 p、 l 操作。
输出
一个整数表示答案。
样例输入
10 3
5 3 1
2 4 1
7 2 2
样例输出
23
提示
对于 30%的数据, 1<=N,M<=1000。
对于 100%的数据, 1<=N<=1000000,1<=M<=5000,0<=p<=p+l< N
看到这个把一堆数放到队首的操作就想到了非旋treap。
要注意的是对于每次修改要利用题目给出的信息转化出新的询问区间。
还要注意答案开long long。
代码:
#include<bits/stdc++.h>
#define N 1000005
#define ll long long
using namespace std;
typedef pair<int,int> res;
inline int read(){
int ans=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
return ans;
}
int n,m,cnt=0;
ll ans=0;
struct Treap{
int rt,val[N],rd[N],son[N][2],siz[N],tot;
inline int newnode(int v){val[++tot]=v,rd[tot]=rand(),siz[tot]=1;return tot;}
inline void pushup(int p){siz[p]=siz[son[p][0]]+siz[son[p][1]]+1;}
inline int merge(int a,int b){
if(!a||!b)return a+b;
if(rd[a]<rd[b])return son[a][1]=merge(son[a][1],b),pushup(a),a;
return son[b][0]=merge(a,son[b][0]),pushup(b),b;
}
inline res split(int p,int k){
if(!p)return make_pair(0,0);
res tmp,ans;
if(siz[son[p][0]]>=k){
tmp=split(son[p][0],k),son[p][0]=tmp.second,pushup(p);
ans.first=tmp.first,ans.second=p;
return ans;
}
tmp=split(son[p][1],k-siz[son[p][0]]-1),son[p][1]=tmp.first,pushup(p);
ans.first=p,ans.second=tmp.second;
return ans;
}
inline void dfs(int p){
if(!p)return;
dfs(son[p][0]);
if((++cnt)&1)ans+=val[p];
dfs(son[p][1]);
}
inline int build(int l,int r){
if(l>r)return 0;
int mid=l+r>>1,p=newnode(mid);
son[p][0]=build(l,mid-1),son[p][1]=build(mid+1,r);
pushup(p);
return p;
}
inline void solve(){
srand(time(NULL));
n=read(),m=read();
rt=build(0,n-1);
while(m--){
int pos=read(),l=read(),r=read(),L,R;
R=pos+l-1,L=(R+1-l*r)%(R+1);
if(L<0)L+=R+1;
if(L>R)continue;
R-=L-1;
res x=split(rt,L),y=split(x.second,R);
rt=merge(y.first,merge(x.first,y.second));
}
dfs(rt);
cout<<ans;
}
}T;
int main(){
T.solve();
return 0;
}