可以发现一条递推式
[a_i=n-1+[a_{i-n}ge n]+[a_{i-n-1}=n+1]
]
那么我们把({n-1, n, n+1})分别标号为({0, 1, 2}),将(a_i)每n个排成一行,当上一行同一位置大于0时对该位置有1的贡献,当上一行前一位置大于1时对该位置有1的贡献。
为了方便边界条件,我们假想每一行末尾多出一个1,那么可以看作每向下一行就把高为2的砖向右平移,然后如果该位置为0就填补上去。
我们把每个坑被填补的时间计算出来,排序,模拟即可。
#define DEBUG
#include <cstdio>
using namespace std;
const int maxn=150000;
void swap(int &a, int &b) {
int t;
t=a, a=b, b=t;
}
int min(int a, int b) {
return a<b ? a : b;
}
int max(int a, int b) {
return a>b ? a : b;
}
class SegmentTree {
public:
int a[4*maxn+1][6], v[4*maxn+1][6], mark[4*maxn+1], flag[4*maxn+1], p;
void markDown(int o, int l, int r) {
if (mark[o]) {
if (flag[o]) {
flag[o] = 0;
}
if (l!=r) {
mark[o*2] = mark[o*2+1] = mark[o];
}
a[o][1] = mark[o], v[o][1] = r-l+1;
for (int i=2; i<=p; i++) {
a[o][i] = v[o][i] = 0;
}
mark[o] = 0;
} else if (flag[o]) {
if (l!=r) {
if (mark[o*2]) {
mark[o*2] += flag[o];
} else {
flag[o*2] += flag[o];
}
if (mark[o*2+1]) {
mark[o*2+1] += flag[o];
} else {
flag[o*2+1] += flag[o];
}
}
for (int i=1; i<=p; i++) {
a[o][i]+=flag[o];
}
flag[o] = 0;
}
}
void update(int a[], int v[], int a1[], int v1[], int a2[], int v2[]) {
for (int i=1; i<=p; i++) {
a[i]=v[i]=0;
}
int maxcnt=0;
for (int i=1; i<=p; i++) {
if (a1[i]) {
int cnt=v1[i], t=a1[i];
for (int j=1; j<=p; j++) {
if (a2[j]==t) {
cnt+=v2[j];
break;
}
}
for (int j=1; j<=p; j++) {
if (v[j]<=cnt) {
swap(a[j], t);
swap(v[j], cnt);
}
}
maxcnt = max(maxcnt, cnt);
}
}
for (int i=1; i<=p; i++) {
if (a2[i]) {
int cnt=0, t=a2[i];
for (int j=1; j<=p; j++) {
if (a1[j]==t) {
cnt = 1;
break;
}
}
if (!cnt) {
cnt=v2[i];
for (int j=1; j<=p; j++) {
if (v[j]<=cnt) {
swap(a[j], t);
swap(v[j], cnt);
}
}
maxcnt = max(maxcnt, cnt);
}
}
}
for (int i=1; i<=p; i++) {
if (a[i]) {
v[i]-=maxcnt;
}
}
}
void set(int o, int l, int r, int tl, int tr, int tv) {
markDown(o, l, r);
if (l==tl && r==tr) {
mark[o] = tv;
markDown(o, l, r);
} else {
int mid=(l+r)/2;
if (tl<=mid) {
set(o*2, l, mid, max(tl, l), min(tr, mid), tv);
} else {
markDown(o*2, l, mid);
}
if (tr>mid) {
set(o*2+1, mid+1, r, max(tl, mid+1), min(tr, r), tv);
} else {
markDown(o*2+1, mid+1, r);
}
update(a[o], v[o], a[o*2], v[o*2], a[o*2+1], v[o*2+1]);
}
}
void add(int o, int l, int r, int tl, int tr) {
markDown(o, l, r);
if (l==tl && r==tr) {
flag[o]++;
markDown(o, l, r);
} else {
int mid=(l+r)/2;
if (tl<=mid) {
add(o*2, l, mid, max(tl, l), min(tr, mid));
} else {
markDown(o*2, l, mid);
}
if (tr>mid) {
add(o*2+1, mid+1, r, max(tl, mid+1), min(tr, r));
} else {
markDown(o*2+1, mid+1, r);
}
update(a[o], v[o], a[o*2], v[o*2], a[o*2+1], v[o*2+1]);
}
}
void get(int o, int l, int r, int tl, int tr, int ans[], int ansv[]) {
markDown(o, l, r);
if (l==tl && r==tr) {
int temp[6], tempv[6];
for (int i=1; i<=p; i++) {
temp[i] = ans[i], tempv[i] = ansv[i];
}
update(ans, ansv, temp, tempv, a[o], v[o]);
} else {
int mid=(l+r)/2;
if (tl<=mid) {
get(o*2, l, mid, max(tl, l), min(tr, mid), ans, ansv);
}
if (tr>mid) {
get(o*2+1, mid+1, r, max(tl, mid+1), min(tr, r), ans, ansv);
}
}
}
};
int main() {
freopen("war.in", "r", stdin);
freopen("war.out", "w", stdout);
static SegmentTree sgt;
int n, m, p;
scanf("%d %d %d", &n, &m, &p);
sgt.p = 100/p;
for (int i=1; i<=n; i++) {
int a;
scanf("%d", &a);
sgt.set(1, 1, n, i, i, a);
}
for (int i=1; i<=m; i++) {
int opt, l, r, id;
scanf("%d %d %d", &opt, &l, &r);
if (opt==1) {
scanf("%d", &id);
sgt.set(1, 1, n, l, r, id);
} else if (opt==2) {
sgt.add(1, 1, n, l, r);
} else if (opt==3) {
static int ans[6], ansv[6];
for (int i=1; i<=sgt.p; i++) {
ans[i]=ansv[i]=0;
}
sgt.get(1, 1, n, l, r, ans, ansv);
int cnt=0;
for (int i=1; i<=sgt.p; i++) {
if (ans[i]) {
cnt++;
}
}
printf("%d ", cnt);
for (int i=1; i<=cnt; i++) {
printf("%d ", ans[i]);
}
printf("
");
}
}
fclose(stdin);
fclose(stdout);
return 0;
}