A Beautiful
题目大意 : 一个n×n的矩阵,每行是排列,要求相邻两行要错排,给出一个矩阵问按字典序从小到大排序后的排名
-
枚举在哪一行字典序小,前面的与给的一样,后面的任意排,再枚举这一行中哪一个字典序小,前面的与给的一样,后面的任意排
-
这样就能求出比这个矩阵字典序小的有多少个
-
具体求的时候,第一行是个排列,康托展开模板,没听说过应该也会
-
后面的行都得错排,枚举哪一个的时候后面的不一定都要错排,要统计一下需要错排的有几个,然后用扩展错排,打表可以找到规律,网上一篇题解上的式子是错的。
Code
Show Code
#include <cstdio>
using namespace std;
const int N = 2005, M = 19990921;
int read(int x = 0, int f = 1, char c = getchar()) {
for (; c < '0' || c > '9'; c = getchar()) if (c == '-') f = -1;
for (; c >='0' && c <='9'; c = getchar()) x = x * 10 + c - '0';
return x * f;
}
int n, a[N][N], f[N][N], p[N], fac[N], ans;
//f[i][j]:i个数j个限制的错排
struct Tree {
int t[N], v[N];
void Add(int x, int w) {
if (w == -1 && !v[x]) return; v[x] ^= 1;
for (; x <= n; x += x & -x) t[x] += w;
}
int Ask(int x, int w = 0) {
for (; x; x -= x & -x) w += t[x];
return w;
}
void Init() {
for (int i = 1; i <= n; ++i) t[i] = i&-i, v[i] = 1;
}
}t1, t2;
int main() {
freopen("beautiful.in", "r", stdin);
freopen("beautiful.out", "w", stdout);
n = read();
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
a[i][j] = read();
f[0][0] = 1; f[1][1] = 0; f[2][2] = 1;
for (int i = 3; i <= n; ++i)
f[i][i] = 1ll * (i - 1) * (f[i-1][i-1] + f[i-2][i-2]) % M;
for (int i = 1; i <= n; ++i)
for (int j = i-1; j >= 0; --j)
if ((f[i][j] = f[i][j+1] + f[i-1][j]) >= M) f[i][j] -= M;
fac[0] = p[0] = 1;
for (int i = 1; i <= n; ++i) {
fac[i] = 1ll * fac[i-1] * i % M;
p[i] = 1ll * p[i-1] * f[n][n] % M;
}
//for (int i = 0; i <= n; ++i, puts("")) for (int j = 0; j <= i; ++j) printf("%d ", f[i][j]);
t1.Init();
for (int j = 1; j <= n; ++j)
t1.Add(a[1][j], -1), ans = (ans + 1ll * t1.Ask(a[1][j]) * fac[n-j]) % M;
ans = 1ll * ans * p[n-1] % M;
for (int i = 2; i <= n; ++i) {
int sum = 0; t1.Init(); t2.Init();
for (int j = 1; j <= n; ++j) {
int tot, cnt, tmp;
//tot 这个位置能选多少个
//tmp 有多少个限制
//cnt 这个位置多少种选法会使限制-1
t1.Add(a[i][j], -1); tot = t1.Ask(a[i][j]);
if (a[i-1][j] < a[i][j] && t1.v[a[i-1][j]]) tot--;
t2.Add(a[i-1][j], -1); tmp = t2.Ask(n);
cnt = t2.Ask(a[i][j] - 1);
sum = (sum + 1ll * cnt * f[n-j][tmp-1] + 1ll * (tot - cnt) * f[n-j][tmp]) % M;
t2.Add(a[i][j], -1);
}
ans = (ans + 1ll * sum * p[n-i]) % M;
}
printf("%d
", ans + 1);
return 0;
}
B Exchange
题目大意 : 支持区间加,区间求和,交换区间
-
开始想的FHQ,但是没学过,突然就想到lct维护,当时感觉特别妙,交换区间的时候Cut四次,然后Link四次,考场上调了3个多小时没调出来
-
正解是动态开点线段树,因为交换的区间有特殊性质,初始区间设为0到2^k-1,交换的时候只需要交换两个节点,真的是秒极了
-
交换完记得Pushup一下,不然查询的时候会出错
Code
Show Code
#include <cstdio>
#include <algorithm>
#define ls t[rt].l
#define rs t[rt].r
using namespace std;
typedef long long ll;
const int N = 2.5e7 + 5;
int read(int x = 0, int f = 1, char c = getchar()) {
for (; (c < '0' || c > '9'); c = getchar()) if (c == '-') f = -1;
for (;!(c < '0' || c > '9'); c = getchar()) x = x * 10 + c - '0';
return x * f;
}
int n, m, M, opt, trc = 1;
ll ans;
struct Tree {
int l, r, tag; ll s;
}t[N];
void Update(int rt, int l, int r, int w) {
t[rt].s += 1ll * w * (r - l + 1); t[rt].tag += w;
}
void Pushdown(int rt, int l, int r) {
if (!ls) ls = ++trc;
if (!rs) rs = ++trc;
if (!t[rt].tag) return;
int mid = l + r >> 1;
Update(ls, l, mid, t[rt].tag);
Update(rs, mid+1, r, t[rt].tag);
t[rt].tag = 0;
}
void Add(int rt, int l, int r, int x, int y, int w) {
if (x <= l && r <= y) return Update(rt, l, r, w);
int mid = l + r >> 1; Pushdown(rt, l, r);
if (x <= mid) Add(ls, l, mid, x, y, w);
if (y > mid) Add(rs, mid+1, r, x, y, w);
t[rt].s = t[ls].s + t[rs].s;
}
int Find(int rt, int l, int r, int x, int y) {
if (x <= l && r <= y) return rt;
int mid = l + r >> 1; Pushdown(rt, l, r);
if (y <= mid) return Find(ls, l, mid, x, y);
else return Find(rs, mid + 1, r, x, y);
}
ll Ask(int rt, int l, int r, int x, int y) {
if (x <= l && r <= y) return t[rt].s;
int mid = l + r >> 1; ll ans = 0; Pushdown(rt, l, r);
if (x <= mid) ans = Ask(ls, l, mid, x, y);
if (y > mid) ans += Ask(rs, mid + 1, r, x, y);
return ans;
}
int main() {
freopen("exchange.in", "r", stdin);
freopen("exchange.out", "w", stdout);
n = read(); m = read(); opt = read(); M = n + 1;
for (int i = 1; i <= n; i *= 2)
if (i * 2 - 1 > n) n = i * 2 - 1;
while (m--) {
int od = read(), l = read(), r = read();
if (opt) l = (l + ans) % M, r = (r + ans) % M;
if (od == 1) {
int x = read();
if (opt) x = (x + ans) % M;
Add(1, 0, n, l, r, x);
}
else if (od == 2) {
int x = read();
if (opt) x = (x + ans) % M;
x = 1 << x; l *= x; r *= x;
swap(t[Find(1, 0, n, l, l + x - 1)], t[Find(1, 0, n, r, r + x - 1)]);
Add(1, 0, n, l, l + x - 1, 0);
Add(1, 0, n, r, r + x - 1, 0);
}
else printf("%lld
", ans = Ask(1, 0, n, l, r));
}
return 0;
}
C Embedding Enumeration (Unaccepted)
题目大意 :
- 巨型分类讨论dp
Code
Show Code