某蒟蒻又来写题解了,今天补题效率挺高, 可能是因为这场比较简单吧,今天依然选择倒序讲题
题目大意 : 给定一个 n n n 个结点的无向完全图, 边权为 1 1 1 ~ k k k, 给出以下定义: 如果与 1 1 1 相连的边都是最小生成树中的边, 那么这样的图,就称为完全美丽图, 换句话来说, 最小生成树的边 都要与 1 1 1 相连
看到数据范围, 能够感觉出要用
D
P
DP
DP
我们从小到大依次枚举图中的最小边权
k
k
k, 因此每条边的边权可以取的范围就是
k
k
k ~
w
w
w , 我们可以预处理出
w
−
k
+
1
w - k + 1
w−k+1 的 次幂,
我们可以给出以下的状态定义
状态表示
f
[
k
]
[
i
]
f[k][i]
f[k][i]: 选出
i
i
i 条边, 并且最大边权为
k
k
k 的方案数
状态计算:
f
[
w
]
[
i
+
j
]
=
(
f
[
w
]
[
i
+
j
]
+
f
[
w
−
1
]
[
i
]
∗
C
[
n
−
i
−
1
]
[
j
]
∗
p
o
w
w
−
k
+
1
[
i
∗
j
+
j
∗
(
j
−
1
)
/
2
]
)
f[w][i + j] = (f[w][i + j] + f[w - 1][i] * C[n - i - 1][j] * pow_{w- k + 1}[i* j + j * (j - 1) / 2]) % mod
f[w][i+j]=(f[w][i+j]+f[w−1][i]∗C[n−i−1][j]∗poww−k+1[i∗j+j∗(j−1)/2])
首先解释一下这个状态转移方程, 在最小生成树中, 我们先选出 i i i 条边,最大的边权为 w w w 再选出 j j j 条边,这些边的边权都为w + 1, C [ n − i + 1 ] [ j ] C[n - i + 1][j] C[n−i+1][j] 的意思就是 已经选出了 i i i 条边, 总共有 n − 1 n - 1 n−1 条边, 还剩下 n − i + 1 n - i + 1 n−i+1 条边没选, 在剩下的边中选 j j j 条边, p o w w − k + 1 [ i ∗ j + j ∗ ( j − 1 ) / 2 ] pow_{w- k + 1}[i* j + j * (j - 1) / 2] poww−k+1[i∗j+j∗(j−1)/2] 的意思是, 因为在最小生成树中最大边权为 k k k,所以其他边的边权必须要大于等于 k + 1 k + 1 k+1 ,所以其他边可以取的值为 k k k ~ w w w, 一共 w − k + 1 w - k + 1 w−k+1 种, i ∗ j i * j i∗j 就是 i , j i,j i,j 之间边的数量, j * (j - 1) / 2 就是 j j j 条边之间边的数量
因为每层状态只用到了上一层的状态, 所以可以进行降维操作
f [ i + j ] = ( f [ i + j ] + f [ i ] ∗ C [ n − i − 1 ] [ j ] ∗ p o w w − k + 1 [ i ∗ j + j ∗ ( j − 1 ) / 2 ] ) f[i + j] = (f[i + j] + f[i] * C[n - i - 1][j] * pow_{w- k + 1}[i* j + j * (j - 1) / 2]) % mod f[i+j]=(f[i+j]+f[i]∗C[n−i−1][j]∗poww−k+1[i∗j+j∗(j−1)/2])
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define x first
#define y second
typedef pair<int,int>PII;
#define debug printf("debug\n");
const int N = 5e5 + 10, INF = 0x3f3f3f3f, mod = 998244353;
int t;
int f[N], C[300][300],pow1[N], dp[N];
void solve()
{
int n, k;
cin >> n >> k;
f[0] = 1;
for(int i = 0; i <= n; i ++ ) //预处理组合数
{
C[i][i] = C[i][0] = 1;
for(int j = 1; j < i; j ++ )
C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % mod;
}
for(int w = 1; w <= k; w ++ ) //枚举前j条边中的最大边权
{
for(int i = 0; i < n; i ++ ) dp[i] = f[i], f[i] = 0; //备份上一层的状态
//这里为什么要用备份? 因为我们更新的时候要用到上一层的状态, 更新的状态用的是这一层的状态
pow1[0] = 1; //初始化
for(int i = 1; i <= n * n; i ++ ) pow1[i] = pow1[i - 1] * (k - w + 1) % mod; //预处理出k - w + 1的次幂
for(int i = 0; i < n; i ++ ) //枚举i条边 这里的边是最小生成树中的边
for(int j = 0; j < n; j ++) //枚举j条边
if(i + j <= n - 1) //必须合法
f[i + j] = (f[i + j] + dp[i] * C[n - i - 1][j] * pow1[i * j + j * (j - 1) / 2] ) % mod;
}
cout << f[n - 1] << '\n';
}
signed main()
{
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
//cin >> t;
solve();
}
题意分析: 你有
C
C
C 个金币, 有
n
n
n 种 士兵, 有
m
m
m 个怪物, 对于每一个怪物, 你只能雇佣一种士兵, 不限个数, 但是每种士兵的雇佣的总价格价格不能超过
C
C
C, 每种士兵的价格,伤害, 血量为
c
,
d
,
h
,
c,d,h,
c,d,h, 怪物的伤害,血量为
d
,
h
d,h
d,h
对于一个怪物, 如果不能打赢他则输出
−
1
-1
−1, 能则输出需要雇佣士兵的最小金币数, 士兵与怪物的战斗是持续的,并且同时攻击,而不是每秒攻击一次
不难看出, 每种士兵不限个数, 有点像完全背包, 当然, 我们可以这么去处理
首先, 因为战斗是持续的, 所以
h
a
/
d
b
>
h
b
/
d
a
h_a / d_ b > h_b / d_a
ha/db>hb/da, 可以转化成
h
a
∗
d
a
>
h
b
∗
d
b
h_a * d_a > h_b * d_b
ha∗da>hb∗db
状态表示:
f
[
i
]
f[i]
f[i]: 用恰好
i
i
i 个金币, 造成的伤害的集合(这里的伤害指的是
h
h
h 和
d
d
d 的乘积)
状态属性: 伤害的最大值
状态计算 :
f
[
j
]
=
m
a
x
(
f
[
j
]
,
f
[
i
]
∗
j
/
i
)
;
f[j] = max(f[j], f[i] * j / i);
f[j]=max(f[j],f[i]∗j/i);
对于这个状态转移方程,
j
j
j 是代表雇佣某一个兵种的总花费,
i
i
i 是该兵种的单价
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 5e6 + 10;
int f[N];
void solve()
{
int n, C, m;
cin >> n >> C;
for(int i = 1; i <= n; i ++ ) //初始化将伤害值存入数组
{
int h, c, d;
cin >> c >> d >> h;
f[c] = max(f[c], d * h);
}
for(int i = 1; i <= C; i ++ ) //雇佣士兵的单价
{
f[i] = max(f[i], f[i - 1]); //不雇佣这种士兵, 直接继承上一层的状态
for(int j = 2 * i; j <= C; j += i ) //雇佣这种士兵的总价格
f[j] = max(f[j], f[i] * j / i);
}
// cout << f[C] << '\n';
cin >> m;
while(m -- )
{
int h, d;
cin >> d >> h;
if(d * h >= f[C]) //在不超过C个金币的前提下,不存在能打过该怪物的情况
cout << -1<< '\n';
//二分查找 找到第一个大于d * h 的最小值
else cout << upper_bound(f + 1, f + 1 + C, d * h) - f << ' ';
}
}
signed main()
{
solve();
}
题意分析: 就是给定一个括号序列, 对于括号序列中, 如果存在最短的前缀满足以下条件之一:
- 是常规的括号序列(即’(‘与’)’ 一 一 配对)
- 是长度至少为 2 2 2 的回文串
可以进行一次操作直接将其删掉, 注意是删去最短的!
不难发现,对于一个括号序列, 只存在
4
4
4 种情况,
(
)
()
(),
)
(
)(
)(,
(
(
((
((,
)
)
))
)), 这四种中 只有
)
(
)(
)( 不能直接删去, 在这种情况下, 再遇到
)
)
) 就可以删去了
所以删去前缀的条件有两种:
- 出现 ′ ( ′ '(' ′(′
- 遇到相同的 ′ ) ′ ')' ′)′
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define x first
#define y second
typedef pair<int,int>PII;
#define debug printf("debug\n");
const int N = 5e5 + 10, INF = 0x3f3f3f3f;
int T;
void solve()
{
int n, res = 0;
cin >> n;
string arr;
cin >> arr;
vector<char>s;
for(int i = 0; i < (int)arr.size(); i ++ )
{
s.push_back(arr[i]); //插入
if(s.size() == 1) continue; //只剩一个时, 不能删
//1. 遇到( 直接删, 2. 遇到相同, 直接删
if( *s.begin() == '(' || (*s.begin() == s.back()))
{
s.clear();
res ++;
}
}
cout << res << " " << s.size() << '\n';
}
signed main()
{
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> T;
while(T -- )
solve();
}
题意分析: 给定一个长度为
n
+
1
n + 1
n+1 的序列,
a
0
=
0
a_0 = 0
a0=0, 请你给
a
1
a_1
a1 ~
a
n
a_n
an 赋值, 使得他们每一项都小于等于
B
B
B, 并且总和最大
赋值规则如下:
a
i
=
a
i
−
1
+
X
a_i = a_ {i - 1} + X
ai=ai−1+X或者
a
i
=
a
i
−
1
−
Y
a_i = a_{i - 1} - Y
ai=ai−1−Y
不难发现我们可以先一直加 X X X, 当发现加 X X X 超过了 B B B 时, 就减去 Y Y Y, 然后又加X, 总之就是 能加就加, 不能加就减
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define x first
#define y second
typedef pair<int,int>PII;
#define debug printf("debug\n");
const int N = 1e5 + 10, INF = 0x3f3f3f3f;
int t;
void solve()
{
int n, B, x, y;
cin >> n >> B >> x >> y; //读入
int sum = 0, num = 0;
for(int i = 1; i <= n; i ++ )
{
if(num + x > B) //不能加就减
num -= y;
else //能加就加
num += x;
sum += num;
}
cout << sum << '\n';
}
signed main()
{
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> t;
while(t -- )
solve();
}
题意分析: 从 ( 0 , 0 ) (0,0) (0,0) 点出发, 每次任意移动, 但是移动必须满足欧几里得距离必须为完全平方数, 请问走到点 ( x , y ) (x,y) (x,y) 至少需要多少步
分析: 首先, 终点就是 ( 0 , 0 ) (0,0) (0,0) 直接返回 0 0 0, 然后如果终点到起点的欧几里得距离就是完全平方数,则可以直接跳到终点, 返回 1 1 1, 否则返回 2 2 2, 因为我们可以先跳到 ( x , 0 ) (x,0) (x,0), 再跳到 ( x , y ) (x,y) (x,y)
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define x first
#define y second
typedef pair<int,int>PII;
#define debug printf("debug\n");
const int N = 1e5 + 10, INF = 0x3f3f3f3f;
int t;
void solve()
{
int x, y;
cin >> x >> y;
if(x == 0 && y == 0 ) cout << 0 << '\n';
else
{
bool flag = 0; //标记
int sum = x * x + y * y; //欧几里得距离
for(int i = 1; i <= sum; i ++ )
if(i * i == sum)
{
flag = 1;
break;
}
if(flag) cout << 1 << '\n'; //直接是完全平方数
else cout << 2 << '\n';
}
}
signed main()
{
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> t;
while(t -- )
solve();
}
完结啦!!! 给个赞吧, 不容易哦