LGP4365 九省联考 2018 秘密袭击 coat
原题链接:九省联考 2018 秘密袭击 coat
分析
呃......还是说,别浮躁吧,有点毛燥了...... xy 可能几亿天前就指出了我的这个问题......努力打好每一场比赛,补好每一道题......继续加油吧......
感觉这个好亮啊......
在一颗树上选择一个大小为 s s s 的连通块。求所有可能的连通块的第 k k k 大值之和。
我们知道, 64123 64123 64123 是一个质数。
然后,对于连通块大小 < k <k <k 的,我们没有必要进行考虑。
然后呢?我怎么才能做第 k k k 大?
我们看了一下数据范围,发现,可以 O ( n 2 ) O(n^2) O(n2)。
那么,我似乎有了一些头绪。
就是说,我们枚举我们要选择的连通块大小,不妨设其为 s i z siz siz。然后,对于第 k k k 大,我们枚举究竟哪个作为这个第 k k k 大。
那么,还会发生什么呢?呃,我们要找到 k − 1 k-1 k−1 个比这个大的,还需要找到 s i z − k siz-k siz−k 个比它小的......
k = 1 k=1 k=1 我会不会做......就会是说最大值......这个好做一点......欸......难道说,就是说,我在倒着考虑的时候......似乎就是保留一些大小固定的子树......但是,有不完全正确......
如果是暴力,肯定就是限制个数自己枚举,然后做完了......这个估计是一个 O ( 2 n ) O(2^n) O(2n) 的,可能还需要 n log n n\log n nlogn 求一下连通块的个数,区别不是很大......期望获得 15 15 15 分。
对于保证地图是一条链,可以直接上主席树吗?好像不是很行,因为这个好像是 O ( n 3 log n ) O(n^3\log n) O(n3logn) 的......但是,开了 7 s 7s 7s 欸......算一下......别急,这个真的是 O ( n 2 log n ) O(n^2\log n) O(n2logn) 的!因为我有至多 n × ( n − 1 ) 2 \dfrac{n\times(n-1)}{2} 2n×(n−1) 个区间,单次查询是 O ( log n ) O(\log n) O(logn) 的。
那么,一共期望获得 25 25 25 分。
别急,那么,我能不能直接在 dfn 上枚举,然后主席树维护呢?!那并不能保证是一个连通块,只能保证是一个子树......那么,如果是 bfn 呢?研究以下......
我的颈椎为什么这么疼......特别是现在穿的比较厚,不方便仰头......
lyx 让我尝试一下树分块......如果说这个可以的话......那么,似乎真的做完了!如果吃一坨的话,真的有可能吧......
还是决定看一下题解......
看题解发现......不是,竟然这都行吗?
发现,其实,正解之一是 O ( n 2 w ) O(n^2w) O(n2w) 卡常之后的状物(当然,有线段树+拉差+ DP 的东西,不过,显然不是我能轻易理解的)。
具体的,我们之前的暴力......甚至更优雅一点......
我们直接进行树形 DP,对于那些 > k >k >k 的点看成 1 1 1, < k <k <k 的看成 0 0 0。就做完了???!!!
正解
cpp
#include <bits/stdc++.h>
// #define int long long
#define mod 64123
using namespace std;
const int N = 1700;
int read(){
char c = getchar();
int x = 0, f = 1;
while (c < '0' || c > '9'){
if (c == '-')
f = -1;
c = getchar();
}
while (c >= '0' && c <= '9'){
x = x * 10 + c - '0';
c = getchar();
}
return x * f;
}
void write(int x){
if (x > 9){
write(x / 10);
}
putchar(x % 10 + '0');
}
int n, k, w;
int dp[N][N];
int d[N];
vector<int> e[N];
int rt;
void dfs(int u, int fa){
if (d[u] > d[rt] || (d[u] == d[rt] && u < rt)){
for (int i = 1; i < k; i++){
dp[u][i + 1] = dp[u][i + 1] + dp[fa][i];
if (dp[u][i + 1] >= mod)
dp[u][i + 1] -= mod;
}
}
else{
for (int i = 1; i <= k; i++){
dp[u][i] = dp[u][i] + dp[fa][i];
if (dp[u][i] >= mod)
dp[u][i] -= mod;
}
}
for (auto v : e[u]){
if (v == fa)
continue;
dfs(v, u);
}
for (int i = 1; i <= k; i++){
dp[fa][i] = dp[fa][i] + dp[u][i];
if (dp[fa][i] >= mod)
dp[fa][i] -= mod;
}
}
signed main(){
// ios::sync_with_stdio(0);
// cin.tie(0);
// cout.tie(0);
n = read();
k = read();
w = read();
for (int i = 1; i <= n; i++){
d[i] = read();
}
for (int i = 1, u, v; i < n; i++){
u = read();
v = read();
e[u].push_back(v);
e[v].push_back(u);
}
int ans = 0;
for (int i = 1; i <= n; i++){
rt = i;
int cnt = 0;
for (int j = 1; j <= n; j++){
if (d[j] > d[rt] || (d[j] == d[rt] && j < rt)){
cnt++;
}
}
if (cnt < k - 1)
continue;
memset(dp, 0, sizeof(dp));
dp[rt][1] = 1;
for (auto v : e[rt]){
dfs(v, rt);
}
long long res = 1ll * d[rt] * dp[rt][k];
while (res >= mod)
res -= mod;
ans = ans + (int)res;
if (ans >= mod)
ans -= mod;
}
write(ans);
}
CF1992G Ultra-Meow
原题链接:G. Ultra-Meow
分析
昨天我在地铁上似乎会了,我们来把它想清楚,然后写下来!
还有空集!
显然啊,我们的复杂度大概在 O ( ∑ n 2 ) O(\sum{n^2}) O(∑n2)。
难道是预处理?
上了一个厕所,我觉得,难道是枚举一个数 i i i,在出现在 k k k 位的 MEX ( { ... } , k ) \operatorname{MEX}(\{\dots\},k) MEX({...},k) 的个数?
这个似乎使用一些组合数是可以做的......
比方说我们认为 i i i 是这个未出现的第 k k k 个数,那么,其个数就应该是:
( i − 1 k − 1 ) \dbinom{i-1}{k-1} (k−1i−1)
我不会给切了吧......
但是,我还是没有推出来总式:
∑ i = 1 n + 1 ∑ k = 2 n + 1 i × ( i − 1 i − k − 1 ) × ( n − i k − ( i − k − 1 ) ) \sum\limits_{i=1}^{n+1}{\sum\limits_{k=2}^{n+1}{i\times\tbinom{i-1}{i-k-1}}}\times\tbinom{n-i}{k-(i-k-1)} i=1∑n+1k=2∑n+1i×(i−k−1i−1)×(k−(i−k−1)n−i)
即,我们需要选一些位置,同时,在整个域当中,还有不选的,组合数乘起来,就做完了......
怎么说呢?虽然 lyx 有时候说话不好听,但是,真的帮助我了......也可以看出来,他很纯粹......
虽然上面的式子是对的,但是,我们还可以直接思考有多少个比 i i i 大/小,然后直接计数。
那么,最终的式子就是:
∑ i = 1 2 n + 1 ∑ k = ⌈ ( i − 1 ) / 2 ⌉ i − 1 i × ( min ( i − 1 , n ) i − k − 1 ) × ( max ( 0 , n − i ) 2 k − i + 1 ) \sum_{i=1}^{2n+1}\ \sum_{k=\lceil (i-1)/2\rceil}^{i-1}\ i\times\binom{\min(i-1,\;n)}{i-k-1}\times\binom{\max(0,\;n-i)}{2k-i+1} i=1∑2n+1 k=⌈(i−1)/2⌉∑i−1 i×(i−k−1min(i−1,n))×(2k−i+1max(0,n−i))
控制好边界就没有任何问题了。
正解
cpp
#include <bits/stdc++.h>
#define int long long
#define mod 1000000007
using namespace std;
const int N = 5005;
int t;
int n;
int C[N][N];
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
C[0][0] = 1;
for (int i = 1; i < N; i++){
C[i][0] = 1;
for (int j = 1; j <= i; j++){
C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mod;
}
}
cin >> t;
for (int cs = 1; cs <= t; cs++){
cin >> n;
int ans = 0;
for (int i = 1; i <= 2 * n + 1; i++){
int tmp0 = min(i - 1, n);
int tmp1 = max(0ll, n - i);
for (int k = i / 2; k <= i - 1; k++){
int tmp2 = 2 * k - i + 1;
if (tmp2 < 0 || tmp2 > tmp1)
continue;
ans = (ans + i * C[tmp0][i - k - 1] % mod * C[tmp1][tmp2]) % mod;
}
}
cout << ans << "\n";
}
}
QOJ8006 Dinosaur Bones Digging
分析
感觉这题卡我几亿年了......实在不行,贺了吧......
别急,难道这个真的是我可以自己写出来的?
要相信自己,对......明天把线段树合并、二分、分治学了......然后再往后准备开一个莫队之类的东西......
首先,我觉得你应该写一个 O ( q n l o g n ) O(qnlogn) O(qnlogn) 左右的暴力来验证自己是不是真的看懂体面了。
不是,样例在说啥???为啥他要 × 6 \times 6 ×6 啊???
不是,他样例分明就是错的!那我写个毛线啊!
直接贺了!
所以,为啥只输出一个?
正解
cpp
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1000005;
const int inf = 0x7f7f7f7f;
int n, a[N], q;
pair<int, int> b[N];
struct segtree{
struct tree{
int mx, tag;
}tr[N << 2];
void pushup(int p){
tr[p].mx = max(tr[p << 1].mx, tr[p << 1 | 1].mx);
}
void down(int p){
if (!tr[p].tag)
return ;
tr[p << 1].tag += tr[p].tag;
tr[p << 1].mx += tr[p].tag;
tr[p << 1 | 1].tag += tr[p].tag;
tr[p << 1 | 1].mx += tr[p].tag;
tr[p].tag = 0;
}
void modify(int p, int l, int r, int s, int t, int val){
if (s > t)
return ;
if (s <= l && r <= t){
tr[p].mx += val;
tr[p].tag += val;
return ;
}
down(p);
int mid = (l + r) >> 1;
if (s <= mid)
modify(p << 1, l, mid, s, t, val);
if (t > mid)
modify(p << 1 | 1, mid + 1, r, s, t, val);
pushup(p);
}
int query(int p, int l, int r, int s, int t){
if (s <= l && r <= t)
return tr[p].mx;
int mid = (l + r) >> 1;
down(p);
int res = -inf;
if (s <= mid)
res = max(res, query(p << 1, l, mid, s, t));
if (t > mid)
res = max(res, query(p << 1 | 1, mid + 1, r, s, t));
return res;
}
}T;
int R[N];
pair<int, int> c[N];
void work(int pos, int val){
int ll = 0, rr = 0;
int l = 1, r = q, p = q + 1;
while (l <= r){
int mid = (l + r) >> 1;
if (R[mid] >= pos){
r = mid - 1;
p = mid;
}
else{
l = mid + 1;
}
}
ll = p;
l = 1, r = q, p = 0;
while (l <= r){
int mid = (l + r) >> 1;
if (b[mid].first <= pos){
l = mid + 1;
p = mid;
}
else{
r = mid - 1;
}
}
rr = p;
T.modify(1, 1, q, ll, rr, val);
}
bool cmp(pair<int, int> tmp1, pair<int, int> tmp2){
if (tmp1.first == tmp2.first)
return tmp1.second > tmp2.second;
return tmp1.first < tmp2.first;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
for (int i = 1; i <= n; i++){
cin >> a[i];
c[i] = {a[i], i};
}
cin >> q;
for (int i = 1; i <= q; i++){
cin >> b[i].first >> b[i].second;
}
sort(b + 1, b + q + 1, cmp);
for (int i = 1; i <= q; i++){
R[i] = max(R[i - 1], b[i].second);
}
sort(c + 1, c + n + 1);
int ans = 0;
for (int i = n; i; i--){
work(c[i].second, inf);
ans = max(ans, 1ll * (T.query(1, 1, q, 1, q) - inf) * c[i].first);
work(c[i].second, -inf);
work(c[i].second, 1);
}
cout << ans;
}