题目链接
初始有n列木块,第 i 列有 a[i] 个。
有两种操作:
1 x y : 向左推第x列的从下到上第y个,(如上图所示)
2 x :查询第x列有多少个木块
用平衡树维护,我用的是fhq treap (因为我只会这个,我好菜hhhh)
操作二就是找第x个值是多少,很简单。。
我这里是把树按大小分裂。第一个树L有x - 1个数,剩下的在另一棵树R里,然后把R再分裂,按1分裂,分裂成Y,Z。然后Y就只有x这个节点了。返回x的权值就好了。
看操作一:
往左推一下,然后设可以推动的这个区间是 l ~ r ,l 左边的第一个数是L。
于是 L这个位置的数量就加 l 位置上的数量减去y - 1就好了。
r 这个位置变成了y - 1.
其他的往左平移一个就好,
主要是这个平移怎么实现?
…因为这是fhq treap 直接把l位置上的值变为y - 1,然后合并的时候,把 l 位置跟l + 1 ~ r位置的换一下,也就是合并写成 merge(y,x) ;把 l 放在右边就好了。
注意:
有不能移动的情况:
1、 最左边的木块不能向左移动。(也就是如果分裂后左边区间的最小值大于等于y不能移动)
2、 如果 y 大于x位置的数量的时候不能移动。
我可真是菜啊。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<double,double> pdd;
typedef unsigned long long ull;
typedef unordered_set<int>::iterator sit;
#define st first
#define sd second
#define mkp make_pair
#define pb push_back
void tempwj(){freopen("P2633_1.in","r",stdin);freopen("P2633_1.out","w",stdout);}
ll gcd(ll a,ll b){return b == 0 ? a : gcd(b,a % b);}
ll qpow(ll a,ll b,ll mod){a %= mod;ll ans = 1;while(b > 0){if(b & 1)ans = ans * a % mod;a = a * a % mod;b >>= 1;}return ans;}
struct cmp{bool operator()(const pii & a, const pii & b){return a.second > b.second;}};
int lb(int x){return x & -x;}
//friend bool operator < (Node a,Node b) 重载
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1e9+7;
const int maxn = 4e5+10;
const int M = 1e6 + 2;
std::mt19937 rnd(233);
struct Node
{
int l,r;
ll sum;
int key;
int siz;
ll val;
ll minn;
}node[maxn];
int cnt, root;
int newnode(int val)
{
node[++cnt].val = val;
node[cnt].minn = val;
node[cnt].sum = val;
node[cnt].key = rnd();
node[cnt].siz = 1;
return cnt;
}
void update(int no)
{
node[no].sum = node[node[no].l].sum + node[node[no].r].sum + node[no].val;
node[no].minn = min(node[no].val, min(node[node[no].l].minn,node[node[no].r].minn));
node[no].siz = node[node[no].l].siz + node[node[no].r].siz + 1;
}
void split(int no,int siz,int& x,int& y)
{
if(no == 0)
{
x = 0;
y = 0;
return;
}
if(node[node[no].l].siz + 1 <= siz)
{
x = no;
split(node[no].r,siz - node[node[no].l].siz - 1,node[no].r,y);
}
else
{
y = no;
split(node[no].l,siz,x,node[no].l);
}
update(no);
}
int merge(int x,int y)
{
if(x == 0 || y == 0)
return x + y;
if(node[x].key < node[y].key)
{
node[x].r = merge(node[x].r,y);
update(x);
return x;
}
else
{
node[y].l = merge(x,node[y].l);
update(y);
return y;
}
}
void split_m(int no,ll val,int& x,int& y)
{
if(no == 0)
{
x = 0;
y = 0;
return;
}
if(node[no].val < val || node[node[no].r].minn < val)
{
x = no;
split_m(node[no].r,val,node[no].r,y);
}
else
{
y = no;
split_m(node[no].l,val,x,node[no].l);
}
update(no);
}
ll query(int pos)
{
int x,y,z;
split(root,pos - 1,x,y);
split(y,1,y,z);
ll ans = node[y].val;
root = merge(x,merge(y,z));
return ans;
}
ll solve(int pos,ll val)
{
if(query(pos) < val)
return 0;
int x1,y1;
split(root,pos,x1,y1);
//把pos左边区间的全放在x1里
if(node[x1].minn >= val)
{
merge(x1,y1);
return 0;
}
int x2,y2;
split_m(x1,val,x2,y2);
//找到最右边小于val的地方的右区间放在y2里,其他的在x2里
ll ans = node[y2].sum - 1ll * node[y2].siz * (val - 1);
int x3,y3;
split(x2,node[x2].siz - 1,x3,y3);
// y3放的是要移动的最左边的这个点的左边
int x5,y5;
split(y2,1,x5,y5);
// x5放的是要移动的左边的这个点
ll s = node[x5].val - val + 1;
node[y3].val += s;
node[y3].sum = node[y3].val;
node[y3].minn = node[y3].val;
node[x5].val = val - 1;
node[x5].sum = node[x5].val;
node[x5].minn = node[x5].val;
root = merge(merge(merge(x3,y3),merge(y5,x5)),y1);
return ans;
}
int a[maxn];
void add(int val,int pos)
{
root = merge(root,newnode(val));
}
std::vector<ll> vv;
void zxbl(int no)
{
if(no == 0)
return;
zxbl(node[no].l);
vv.pb(node[no].val);
zxbl(node[no].r);
}
int main()
{
int T;
scanf("%d",&T);
while(T -- )
{
node[0].minn = INF;
cnt = 0;
root = 0;
int n,m;
scanf("%d%d",&n,&m);
for (int i = 1; i <= n; i ++ )
{
scanf("%d",&a[i]);
}
for (int i = 1; i <= n; i ++ )
{
add(a[i],i);
}
// zxbl(root);
// printf("\n");
while(m -- )
{
int f;
scanf("%d",&f);
if(f == 1)
{
int x,y;
scanf("%d%d",&x,&y);
printf("%lld\n",solve(x,y));
}
else
{
int x;
scanf("%d",&x);
printf("%lld\n",query(x));
}
// zxbl(root);
// printf("\n");
}
zxbl(root);
for (int i= 0; i < vv.size(); i ++ )
{
if(i == 0)
printf("%lld",vv[i]);
else
printf(" %lld",vv[i]);
}
vv.clear();
printf("\n");
for (int i = 1; i <= cnt; i ++ )
{
node[i].l = node[i].r = node[i].siz = node[i].val = node[i].sum = node[i].minn = 0;
}
}
}
找了好长时间的bug,把所有能改的 int 全改成了 longlong 。。。难受,
最后发现少判断了一种不能移动的情况。
我好菜,小菜鸡