ZR19CSP-S赛前冲刺day6
为了保护ZR的版权,这里不提供题目QWQ
http://zhengruioi.com/contest/444 (你进得去吗/xyx)
为什么dls出的题这么毒瘤啊
A 爬杆
首先先假设把所有梯子加上去
发
现
i
,
j
两
点
的
最
短
距
离
就
是
h
i
+
h
j
−
2
min
k
=
i
j
发现i, j 两点的最短距离就是 h_i + h_j - 2\min_{k=i}^{j}
发现i,j两点的最短距离就是hi+hj−2mink=ij
然后50‘的部分分就直接算每一个位置的贡献就可以了
考虑最小加几个梯子
先把笛卡尔树建出来(单调栈)
考虑贪心加梯子
先
把
序
列
的
最
小
值
拿
出
来
,
假
设
位
置
是
r
t
,
它
左
边
区
间
的
最
小
值
的
位
置
是
c
h
[
r
t
]
[
0
]
,
先把序列的最小值拿出来,假设位置是rt, 它左边区间的最小值的位置是ch[rt][0],
先把序列的最小值拿出来,假设位置是rt,它左边区间的最小值的位置是ch[rt][0],
右
边
的
是
c
h
[
r
t
]
[
1
]
,
那
么
r
t
肯
定
是
要
向
c
h
[
r
t
]
[
0
]
和
c
h
[
r
t
]
[
1
]
建
高
度
为
h
[
[
r
t
]
的
桥
,
右边的是ch[rt][1],那么rt 肯定是要向ch[rt][0] 和 ch[rt][1]建高度为h[[rt]的桥,
右边的是ch[rt][1],那么rt肯定是要向ch[rt][0]和ch[rt][1]建高度为h[[rt]的桥,
但
如
果
原
来
就
已
经
有
高
度
为
h
[
r
t
]
的
桥
就
不
用
建
了
但如果原来就已经有高度为h[rt]的桥就不用建了
但如果原来就已经有高度为h[rt]的桥就不用建了
然后这题就没了
具
体
的
做
法
就
是
分
治
,
d
f
s
(
r
t
,
l
,
r
)
表
示
当
前
区
间
的
最
小
值
的
位
置
是
r
t
,
r
t
的
左
边
的
桥
的
高
度
具体的做法就是分治,dfs(rt,l, r)表示当前区间的最小值的位置是rt, rt的左边的桥的高度
具体的做法就是分治,dfs(rt,l,r)表示当前区间的最小值的位置是rt,rt的左边的桥的高度
是
l
,
右
边
的
桥
的
高
度
是
r
,
然
后
按
照
h
[
r
t
]
和
l
,
r
的
大
小
分
情
况
讨
论
一
下
,
然
后
分
治
下
去
就
行
了
是l, 右边的桥的高度是r, 然后按照h[rt]和l,r的大小分情况讨论一下,然后分治下去就行了
是l,右边的桥的高度是r,然后按照h[rt]和l,r的大小分情况讨论一下,然后分治下去就行了
code:
#include<bits/stdc++.h>
#define int long long
#define N 1000005
using namespace std;
int anss, ans, n, ch[N][2], a[N], sum[N], size[N], sta[N], top, T;
void dfs(int rt, int l, int r) {
if(ch[rt][0]) {
if(a[rt] > l) {
anss += rt - ch[rt][0];
dfs(ch[rt][0], l, a[ch[rt][0]]);
} else dfs(ch[rt][0], l, l);
size[rt] += size[ch[rt][0]];
sum[rt] += sum[ch[rt][0]];
}
if(ch[rt][1]) {
if(a[rt] > r) {
anss += ch[rt][1] - rt;
dfs(ch[rt][1], a[ch[rt][1]], r);
} else dfs(ch[rt][1], r, r);
size[rt] += size[ch[rt][1]];
sum[rt] += sum[ch[rt][1]];
}
ans += sum[ch[rt][0]] - size[ch[rt][0]] * a[rt] +
sum[ch[rt][1]] - size[ch[rt][1]] * a[rt] +
(sum[ch[rt][0]] - size[ch[rt][0]] * a[rt]) * size[ch[rt][1]] +
(sum[ch[rt][1]] - size[ch[rt][1]] * a[rt]) * size[ch[rt][0]];
}
signed main() {
scanf("%lld", &n);
for(int i = 1; i <= n; i ++) scanf("%lld", &a[i]), size[i] = 1, sum[i] = a[i];
scanf("%lld", &T);
for(int i = 1; i <= n; i ++) {
while(top) {
if(a[sta[top]] < a[i]) {
ch[sta[top]][1] = i;
break;
} else {
ch[sta[top]][1] = ch[i][0];
ch[i][0] = sta[top];
top --;
}
}
sta[++ top] = i;
}
ans = (n + 1) * n * (n - 1) / 6;
dfs(sta[1], -1, -1);
printf("%lld ", ans);
if(T == 2) printf("%lld", anss);
return 0;
}
B 变换
不会,先咕着
C 游戏
也是道神仙转换题
首先考虑什么情况下先手必胜
对原图进行最大匹配,如果有节点没有被匹配,那么肯定是先手必胜
为什么呢?
先手先选一个没有被匹配的点,然后后手必定移动到一个被匹配的点上,然后接着移动到被匹配的点的对应点上,这样就能保证先手必胜。
可以用反证法证明一个没有被匹配的点必定移动到一个被匹配,假设成立,那最大匹配数应加一,与假设矛盾,因此不成立
还可以用反证法证明从一个没有被匹配的点移动到一个被匹配的点后不会再移动到一个没有被匹配的点。
上述描述的画出来是这样
那肯定可以偏移一位让匹配数+1
这个结论好像对任意无向图都成立
然后再考虑原问题就简单了
考虑树形DP
设
f
[
i
]
[
j
]
表
示
以
i
为
根
的
子
树
,
还
有
j
个
点
没
有
被
匹
配
的
方
案
树
f[i][j]表示以i为根的子树,还有j个点没有被匹配的方案树
f[i][j]表示以i为根的子树,还有j个点没有被匹配的方案树
然后转移就先做个背包,然后看当前节点是否和下面的匹配
然后就没有了
code:
#include<bits/stdc++.h>
#define mod 998244353
#define ll long long
#define N 2005
using namespace std;
struct edge {
int v, nxt;
}e[N << 1];
int p[N], eid;
void init() {
memset(p, -1, sizeof p);
eid = 0;
}
void insert(int u, int v) {
e[eid].v = v;
e[eid].nxt = p[u];
p[u] = eid ++;
}
ll dp[N][N], ha[N], size[N];
void dfs(int u, int fa) {
dp[u][0] = 1;
for(int i = p[u]; i + 1; i = e[i].nxt) {
int v = e[i].v;
if(v == fa) continue;
dfs(v, u);
for(int j = 0; j <= size[u] + size[v]; j ++) ha[j] = 0;
for(int j = size[u]; j >= 0; j --)
for(int k = 0; k <= size[v]; k ++)
ha[j + k] += dp[u][j] * dp[v][k] % mod, ha[j + k] %= mod;
size[u] += size[v];
for(int j = 0; j <= size[u]; j ++) dp[u][j] = ha[j];
}
size[u] ++;
for(int i = 0; i <= size[u]; i ++) ha[i] = dp[u][i];
for(int i = 0; i < size[u]; i ++) {
if(i) ha[i - 1] += dp[u][i], ha[i - 1] %= mod;
else ha[1] += dp[u][i], ha[1] %= mod;
}
for(int i = 0; i <= size[u]; i ++) dp[u][i] = ha[i];
}
int n;
int main() {
init();
scanf("%d", &n);
for(int i = 1; i < n; i ++) {
int u, v;
scanf("%d%d", &u, &v);
insert(u, v);
insert(v, u);
}
dfs(1, 1);
ll ans = 0;
for(int i = 1; i <= n; i ++) ans += dp[1][i], ans %= mod;
printf("%lld", ans);
return 0;
}
总结
调整心态
加快速度