题目:
https://vjudge.net/problem/SCU-3035
题意:
Description
一个长度为 n 的整数序列初始时从左到右为1,2,3,……,n,现在对这个序列进行 m 次操作,每次把 p 到 q 的子序列反转
求最后的序列
Input
输入包含多组测试数据,每组数据第一行为 n m (1 <= n,m <= 50000)
接下来 m 行,每行两个数,为 p q (1 <= p <= q <= n)
思路:
块状链表解决,用splay之类的平衡树更快。翻转区间时,首先把这些区间所在的块分裂开,在待翻转的块内部都打上翻转标记,但是暂时不翻转,然后把这些块的位置翻转一下,翻转时注意块之间的连接。翻转后记得合并零碎的块
#include <bits/stdc++.h>
using namespace std;
const int N = 50000 + 10, block_sz = 400 + 10, block_num = 400 + 10;
queue<int> que;
int head;
struct block
{
int sz, next;
int a[block_sz];
bool rev;
void init()
{
sz = 0, next = -1;
rev = 0;
}
}g[block_num];
int new_block()
{
int t = que.front(); que.pop();
g[t].init();
return t;
}
void del_block(int t)
{
que.push(t);
}
void block_update(int idx)
{
if(g[idx].rev)
{
reverse(g[idx].a, g[idx].a + g[idx].sz);
g[idx].rev = 0;
}
}
void block_split(int idx, int k)
{
if(idx == -1 || g[idx].sz == k) return;
block_update(idx);
int tot = new_block();
memcpy(g[tot].a, g[idx].a + k, sizeof(int) * (g[idx].sz - k));
g[tot].sz = g[idx].sz - k, g[idx].sz = k;
g[tot].next = g[idx].next, g[idx].next = tot;
}
void block_merge(int idx)
{
for(int i = idx; i != -1; i = g[i].next)
{
for(int j = g[i].next; j != -1; j = g[j].next)
{
if(g[i].sz + g[j].sz <= block_sz)
{
block_update(i), block_update(j);
memcpy(g[i].a + g[i].sz, g[j].a, sizeof(int) * g[j].sz);
g[i].sz += g[j].sz, g[i].next = g[j].next;
del_block(j);
}
else break;
}
}
}
void block_locate(int &idx, int &k)
{
while(idx != -1 && k > g[idx].sz)
k -= g[idx].sz, idx = g[idx].next;
}
void block_init(int n)
{
while(! que.empty()) que.pop();
for(int i = 0; i < block_num; i++) que.push(i);
head = new_block();
int idx = head;
int i = 1;
while(i <= n)
{
int sz = min(block_sz, n - i + 1);
for(int j = 0; j < sz; j++)
g[idx].a[j] = j + i;
g[idx].sz = sz;
int tot = new_block();
g[idx].next = tot, idx = g[idx].next;
i += sz;
}
}
void block_rev(int l, int r)
{
int idx = head;
block_locate(idx, l);
block_split(idx, l);
int st = idx, st_next = g[idx].next;
idx = head;
block_locate(idx, r);
block_split(idx, r);
int en = idx, en_next = g[idx].next;
//翻转时先把待翻转区间分裂开,然后待翻转的块内部都打上翻转标记而不立刻翻转,然后把这些块的位置翻转过来
int tmp[block_num], cnt = 0;
for(int i = st_next; i != en_next; i = g[i].next)
{
g[i].rev ^= 1;
tmp[++cnt] = i;
}
reverse(tmp + 1, tmp + 1 + cnt);
tmp[0] = st, tmp[++cnt] = en_next;
for(int i = 0; i <= cnt-1; i++)
g[tmp[i]].next = tmp[i+1];
block_merge(head);
}
void block_print()
{
for(int i = head; i != -1; i = g[i].next)
{
block_update(i);
for(int j = 0; j < g[i].sz; j++)
printf("%d ", g[i].a[j]);
}
printf("\n");
}
int main()
{
int n, m;
while(~ scanf("%d%d", &n, &m))
{
block_init(n);
for(int i = 1; i <= m; i++)
{
int a, b;
scanf("%d%d", &a, &b);
block_rev(a-1, b);
}
block_print();
}
return 0;
}