一开始我写了如下0pts代码,大体思路是每次利用getrnk函数查找序列中最小的数的位置,然后利用懒标记g进行翻转操作,最后将第一个数(最小的数)删除。
#include<bits/stdc++.h>
using namespace std;
int n,m,a[200005],l,r,cnt,root,g[200005];
pair<int,int>b[200005];
string op;
struct node
{
int val,rnd,ls,rs,sz,mn,mp,fa;
}tr[200005];
inline int getnode(int v) {tr[++cnt] = {v,rand(),0,0,1,v,cnt,0}; return cnt;}
inline void pushup(int root)
{
int ls = tr[root].ls;
int rs = tr[root].rs;
tr[root].sz = tr[ls].sz+tr[rs].sz+1;
tr[root].mn = tr[root].val;
tr[root].mp = root;
if (ls)
{
tr[ls].fa = root;
if (tr[ls].mn < tr[root].mn) tr[root].mn = tr[ls].mn,tr[root].mp = tr[ls].mp;
}
if (rs)
{
tr[rs].fa = root;
if (tr[rs].mn < tr[root].mn) tr[root].mn = tr[rs].mn,tr[root].mp = tr[rs].mp;
}
}
inline void pushdown(int root)
{
swap(tr[root].ls,tr[root].rs);
g[root] = 0;
g[tr[root].ls] ^= 1;
g[tr[root].rs] ^= 1;
}
void split(int root,int v,int &x,int &y)
{
if (!root)
{
x = 0,y = 0;
return;
}
if (g[root]) pushdown(root);
if (tr[tr[root].ls].sz < v)
{
x = root;
split(tr[root].rs,v-tr[tr[root].ls].sz-1,tr[root].rs,y);
pushup(root);
}
else
{
y = root;
split(tr[root].ls,v,x,tr[root].ls);
pushup(root);
}
}
int merge(int x,int y)
{
if (!x || !y) return x+y;
if (g[x]) pushdown(x);
if (g[y]) pushdown(y);
if (tr[x].rnd <= tr[y].rnd)
{
tr[x].rs = merge(tr[x].rs,y);
pushup(x);
return x;
}
else
{
tr[y].ls = merge(x,tr[y].ls);
pushup(y);
return y;
}
}
void insert(int v)
{
int z = getnode(v);
root = merge(root,z);
}/*
void prtr(int u)
{
if (g[u]) pushdown(u);
if (tr[u].ls) prtr(tr[u].ls);
cout << u << ' ';
if (tr[u].rs) prtr(tr[u].rs);
}
void prt(int u)
{
if (g[u]) pushdown(u);
if (tr[u].ls) prt(tr[u].ls);
cout << tr[u].val << ' ';
if (tr[u].rs) prt(tr[u].rs);
}*/
int getrnk(int u)
{
int ret = 1+tr[tr[u].ls].sz;
for (; tr[u].fa && (tr[tr[u].fa].ls == u || tr[tr[u].fa].rs == u); u = tr[u].fa)
{
if (tr[tr[u].fa].rs == u)
{
ret += tr[tr[tr[u].fa].ls].sz+1;
}
}
return ret;
}
int query()
{
// prtr(y);
// cout << '\n';
// prt(y);
// cout << '\n';
int ret = getrnk(tr[root].mp);
return ret;
}
signed main()
{
// ios_base::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> b[i].first;
b[i].second = i;
}
sort(b+1,b+n+1);
for (int i = 1; i <= n; i++)
a[b[i].second] = i;
for (int i = 1; i <= n; i++)
insert(a[i]);
for (int i = 1; i <= n; i++)
{
int q = query();
cout << q+i-1 << ' ';
int x,o,y,z;
split(root,q,x,y);
g[x] ^= 1;
split(x,1,o,z);
root = merge(z,y);
//prt(root);
//cout << '\n' << '\n';
}
return 0;
}
经过对拍和测试,发现是因为g数组中存放的懒标记没有及时下放导致错误,理由是如果每次执行如prt这样的携带更新懒标记的测试函数,答案会恢复正确。
Hack样例:
6
4 2 6 5 3 1
多次修改未果后我写出了如下代码:
#include<bits/stdc++.h>
using namespace std;
int n,m,a[200005],l,r,cnt,root,g[200005];
pair<int,int>b[200005];
string op;
struct node
{
int val,rnd,ls,rs,sz,mn,mp,fa;
}tr[200005];
inline int getnode(int v) {tr[++cnt] = {v,rand(),0,0,1,v,cnt,0}; return cnt;}
inline void pushdown(int root)
{
swap(tr[root].ls,tr[root].rs);
g[root] = 0;
g[tr[root].ls] ^= 1;
g[tr[root].rs] ^= 1;
}
inline void pushup(int root)
{
int ls = tr[root].ls;
int rs = tr[root].rs;
tr[root].sz = tr[ls].sz+tr[rs].sz+1;
tr[root].mn = tr[root].val;
tr[root].mp = root;
if (ls)
{
tr[ls].fa = root;
if (tr[ls].mn < tr[root].mn) tr[root].mn = tr[ls].mn,tr[root].mp = tr[ls].mp;
}
if (rs)
{
tr[rs].fa = root;
if (tr[rs].mn < tr[root].mn) tr[root].mn = tr[rs].mn,tr[root].mp = tr[rs].mp;
}
}
void split(int root,int v,int &x,int &y)
{
if (!root)
{
x = 0,y = 0;
return;
}
if (g[root]) pushdown(root);
if (tr[tr[root].ls].sz < v)
{
x = root;
split(tr[root].rs,v-tr[tr[root].ls].sz-1,tr[root].rs,y);
pushup(root);
}
else
{
y = root;
split(tr[root].ls,v,x,tr[root].ls);
pushup(root);
}
}
int merge(int x,int y)
{
if (!x || !y) return x+y;
if (g[x]) pushdown(x);
if (g[y]) pushdown(y);
if (tr[x].rnd <= tr[y].rnd)
{
tr[x].rs = merge(tr[x].rs,y);
pushup(x);
return x;
}
else
{
tr[y].ls = merge(x,tr[y].ls);
pushup(y);
return y;
}
}
void insert(int v)
{
int z = getnode(v);
root = merge(root,z);
}
void prtr(int u)
{
if (g[u]) pushdown(u);
if (tr[u].ls) prtr(tr[u].ls);
cout << u << ' ';
if (tr[u].rs) prtr(tr[u].rs);
}
void prt(int u)
{
if (tr[u].ls) prt(tr[u].ls);
cout << tr[u].val << ' ';
if (tr[u].rs) prt(tr[u].rs);
}
int getrnk(int u)
{
int ret = 1+tr[tr[u].ls].sz;
stack<int>s;
for (int o = u; o; o = tr[o].fa)
s.push(o);
while (!s.empty())
{
int now = s.top();
s.pop();
if (s.empty()) break;
if (g[now]) pushdown(now);
if (tr[now].rs == s.top())
ret += 1+tr[tr[now].ls].sz;
}
return ret;
}
int query()
{
int ret = getrnk(tr[root].mp);
int x,y; //增加部分
split(root,ret,x,y); //+
root = merge(x,y); // +
ret = getrnk(tr[root].mp); //+
return ret;
}
signed main()
{
ios_base::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> b[i].first;
b[i].second = i;
}
sort(b+1,b+n+1);
for (int i = 1; i <= n; i++)
a[b[i].second] = i;
for (int i = 1; i <= n; i++)
insert(a[i]);
for (int i = 1; i <= n; i++)
{
int q = query();
cout << q+i-1 << ' ';
int x,o,y,z;
split(root,q,x,y);
g[x] ^= 1;
split(x,1,o,z);
root = merge(z,y);
}
return 0;
}
形象地描述修改部分,就是将序列掰开再合上,来更新懒标记g数组。Accepted。
但是我不明白为什么会出现这样的错误,我也不明白这种修改方式是否完全正确。故询问。