真是一场养生比赛。
不得不说我识别水题的能力还是比较强的,T3一道裸的主席树秒切了,T2暴力分十分良心,T1暴力只有10分。还是很后悔,这种结论题我总是懒得去推,结果少了别人90,以后还是要保持冷静思考吧。
首先你得把题看懂。
对于一个 n n n的排列,它的贡献就是将它交换有序的最少次数。
我们可以设 f i f_i fi表示前 i i i个数所有方案的贡献,那么考虑 i i i放在哪一位。
直接放在第 i i i位,无需交换,只用加上 f i − 1 f_{i-1} fi−1。
如果不放在第 i i i位,我们就要把 i i i换到第 i i i位,贡献一次。不放第 i i i位就有 i − 1 i-1 i−1种放法,另外 i − 1 i-1 i−1个数的排列是 ( i − 1 ) ! (i-1)! (i−1)!,所以一共有 ( i − 1 ) ( i − 1 ) ! (i-1)(i-1)! (i−1)(i−1)!种方案贡献是 1 1 1,此外另外 i − 1 i-1 i−1个数也要交换,产生贡献 ( i − 1 ) f i − 1 (i-1)f_{i-1} (i−1)fi−1。
于是得到递推式:
f i = i f i − 1 + ( i − 1 ) ( i − 1 ) ! f_i=if_{i-1}+(i-1)(i-1)! fi=ifi−1+(i−1)(i−1)!
然后求逆元除以 n ! n! n!就行了。
预处理阶乘和 f n f_n fn,复杂度 O ( n ) O(n) O(n),回答每次 O ( 1 ) O(1) O(1)。
Code:
#include
#include
#include
const int P = 998244353, N = 1e7 + 7;
int pow(int a, int b)
{
int ret = 1;
while (b)
{
if (b & 1) ret = 1ll * ret * a % P;
a = 1ll * a * a % P, b >>= 1;
}
return ret;
}
int T, n, f[N], fac[N];
int main()
{
//freopen("inverse.in", "r", stdin);
//freopen("inverse.out", "w", stdout);
f[1] = 0, fac[1] = 1;
for (int i = 2; i <= N - 7; i++)
f[i] = (1ll * i * f[i - 1] % P + 1ll * (i - 1) * fac[i - 1] % P) % P, fac[i] = 1ll * fac[i - 1] * i % P;
scanf("%d", &T);
while (T--)
{
scanf("%d", &n);
printf("%d\n", 1ll * f[n] * pow(fac[n], P - 2) % P);
}
fclose(stdin);
fclose(stdout);
return 0;
}
60 % 60\% 60%的数据(莫名其妙拿了 70 p t s 70pts 70pts,出题人人真好):
考虑到 n n n只有 600 600 600, ∑ k \sum k ∑k只有 10000 10000 10000,容易想到一种 O ( n ∑ k ) O(n\sum k) O(n∑k)的优秀 做法。
首先 O ( n 3 ) O(n^3) O(n3)预处理出每个点两两之间的最短路,Then对于每个询问,在桶里标记一下满足条件的点,就OK了。
100 % 100\% 100%的数据,要用bitset
这个自带 1 32 \frac{1}{32} 321常数的黑科技。
bitset
可以理解为一个每一位只能是0或1的数组,但是因为它经过压位,时空复杂度都是 O ( n / 32 ) O(n/32) O(n/32),其中 n n n是数组位数。我们可以利用这个黑科技做这题。
开一个bitset
,其中b[i][j]
存的是与i
距离<=j
的点集。由于与i
距离<=j
的点集不太方便求,我们可以先考虑求与i
距离=j
的点集,然后用前缀和的方式求出与i
距离<=j
的点集。
然后每次询问把所有点对应的bitset
取个并集,集合大小就是答案了。
求最短路的过程用bfs
复杂度是 O ( n ( n + m ) ) = O ( n 2 + n m ) O(n(n+m))=O(n^2+nm) O(n(n+m))=O(n2+nm)。吸了氧跑的还挺快,总的询问是 ∑ k \sum k ∑k,一次询问复杂度是 O ( n / 32 ) O(n/32) O(n/32),时间复杂度就是 O ( n ∑ k 32 ) O(\frac{n\sum k}{32}) O(32n∑k),空间复杂度是 O ( n 2 32 ) O(\frac{n^2}{32}) O(32n2),再加上O2
对STL
的定制优化,就过了。
Tips:由于重边较多,使用vector
存边貌似能更快。
Code:真的暴力
#include
#include
#include
#include
#include
using namespace std;
const int N = 1e3 + 7;
inline int read()
{
int x = 0, f = 0;
char c = getchar();
for (; c < '0' || c > '9'; c = getchar()) if (c == '-') f = 1;
for (; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ '0');
return f ? -x : x;
}
int n, m, q;
vector<int> edge[N];
void add(int u, int v) { edge[u].push_back(v); }
int head, tail, que[N], d[N];
bitset<N> b[N][N], tmp;
void init()
{
n = read(), m = read(), q = read();
for (int i = 1, u, v; i <= m; i++) u = read(), v = read(), add(u, v), add(v, u);
for (int i = 1; i <= n; i++)
{
memset(d, 0, sizeof(d));
d[i] = 1, head = 1, que[tail = 1] = i;
while (head <= tail)
{
int u = que[head++], siz = edge[u].size();
for (int i = 0; i < siz; i++)
if (!d[edge[u][i]])
d[edge[u][i]] = d[u] + 1, que[++tail] = edge[u][i];
}
for (int j = 1; j <= n; j++) if (d[j]) b[i][d[j] - 1][j] = 1;
for (int j = 1; j <= n; j++) b[i][j] |= b[i][j - 1];
}
}
void solve()
{
for (int i = 1; i <= q; i++)
{
int k = read();
tmp.reset();
for (int j = 1, x, y; j <= k; j++) x = read(), y = read(), tmp |= b[x][y];
printf("%d\n", tmp.count());
}
}
int main()
{
//freopen("input", "r", stdin);
//freopen("center.in", "r", stdin);
//freopen("center.out", "w", stdout);
init();
solve();
fclose(stdin);
fclose(stdout);
return 0;
}
这题没穿衣服(真裸)。
看到区间第 k k k大,首先会想到主席树。可是看到新增与删除操作,就做不了了吗?不,伟大的shehui主义主席树是无所不能的 。
由于是在序列左端修改,我们可以考虑倒着建主席树,对于删除操作,我们只需要把该位置对应的主席树全部清空掉,由于一棵主席树新增的节点最多就 O ( l o g n ) O(logn) O(logn)个,这个操作复杂度是 O ( l o g n ) O(logn) O(logn)的。插入操作就新建一棵主席树,查询操作就开个指针记录一下我插入到了哪里,找到实际要查询的区间求行了。
我的清空方法比较暴力,就是树上所有的节点左儿子右儿子都赋为0。最好离散化一下,这样时间空间都会小很多,总复杂度 O ( ( n + q ) l o g 2 ( n + q ) ) O((n+q)log_2(n+q)) O((n+q)log2(n+q))
Code:
#include
#include
#include
#include
using namespace std;
const int N = 4e5 + 7, Q = 2e5 + 7;
inline int read()
{
int x = 0, f = 0;
char c = getchar();
for (; c < '0' || c > '9'; c = getchar()) if (c == '-') f = 1;
for (; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ '0');
return f ? -x : x;
}
int n, q, pt, a[N], tmp[N];
int opt[Q], L[Q], R[Q], K[Q];
int len, arr[N];
int tot, root[N], lson[N * 50], rson[N * 50], sum[N * 50];
void clear(int rt, int l, int r, int po)
{
sum[rt]--; //这句貌似可以不加
if (l == r) return;
int mid = l + r >> 1;
if (po <= mid) clear(lson[rt], l, mid, po);
else clear(rson[rt], mid + 1, r, po);
lson[rt] = rson[rt] = 0;
}
void insert(int &rt, int fa, int l, int r, int po)
{
if (!rt) rt = ++tot;
sum[rt] = sum[fa] + 1;
if (l == r) return;
int mid = l + r >> 1;
if (po <= mid) rson[rt] = rson[fa], insert(lson[rt], lson[fa], l, mid, po);
else lson[rt] = lson[fa], insert(rson[rt], rson[fa], mid + 1, r, po);
}
int getkth(int rt, int fa, int l, int r, int k)
{
if (l == r) return l;
int mid = l + r >> 1, siz = sum[lson[rt]] - sum[lson[fa]];
if (k <= siz) return getkth(lson[rt], lson[fa], l, mid, k);
else return getkth(rson[rt], rson[fa], mid + 1, r, k - siz);
}
void init()
{
n = read(), q = read(), pt = q;
for (int i = 1; i <= n; i++) a[i] = read(), arr[++len] = a[i];
for (int i = 1; i <= q; i++)
{
opt[i] = read();
if (opt[i] == 2) K[i] = read(), arr[++len] = K[i];
else if (opt[i] == 3) L[i] = read(), R[i] = read(), K[i] = read();
}
sort(arr + 1, arr + len + 1);
len = unique(arr + 1, arr + len + 1) - arr - 1;
}
void solve()
{
for (int i = n; i >= 1; i--)
{
a[i] = lower_bound(arr + 1, arr + len + 1, a[i]) - arr;
insert(root[i + q], root[i + q + 1], 1, len, a[i]);
}
for (int i = 1; i <= n; i++) tmp[i] = a[i], a[i] = 0;
for (int i = 1; i <= n; i++) a[i + q] = tmp[i];
for (int i = 1; i <= q; i++)
{
if (opt[i] == 1) ++pt, clear(root[pt], 1, len, a[pt]);
else if (opt[i] == 2)
{
K[i] = lower_bound(arr + 1, arr + len + 1, K[i]) - arr;
insert(root[pt], root[pt + 1], 1, len, K[i]);
a[pt--] = K[i];
}
else
{
int po = getkth(root[pt + L[i]], root[pt + R[i] + 1], 1, len, K[i]);
printf("%d\n", arr[po]);
}
}
}
int main()
{
//freopen("input", "r", stdin);
//freopen("output", "w", stdout);
//freopen("pigeon.in", "r", stdin);
//freopen("pigeon.out", "w", stdout);
init();
solve();
fclose(stdin);
fclose(stdout);
return 0;
}
这是这一周最放松的比赛了吧。