A题 数学期望 待补
B题 分块莫队 待补
F题 FFT 待补
I 题 待补
J题 待补
C题:
题目大意:S1和S2都要到T去 最小化修路的费用
分析:也就是尽量能重合走的就重合走 一定存在中间点mid S1到T 和 S2到T 都要经过mid
所以 最小化dis(S1,mid)+dis(S2,mid)+dis(mid,T) 就好
建正边和反边分别跑一次最短路就好
注意!!!一定不要再用spfa 他死了!!!!但是比赛就是用的spfa坑死了
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
using PII = pair<int, int>;
using PLI = pair<LL, int>;
const LL INF = 1e18;
int main() {
ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
int n, m, s1, s2, t;
cin >> n >> m >> s1 >> s2 >> t;
vector<vector<PII>> G(n), rG(n);
for(int i = 0; i < m; ++ i) {
int u, v, w;
cin >> u >> v >> w;
G[u].push_back({v, w});
rG[v].push_back({u, w});
}
auto dijkstra = [&](int s, vector<vector<PII>> &g) -> vector<LL> {
vector<bool> vis(n, 0);
vector<LL> d(n, INF);
priority_queue<PLI, vector<PLI>, greater<PLI>> Q;
d[s] = 0;
Q.push({0, s});
while(!Q.empty()) {
auto u = Q.top().second; Q.pop();
if(vis[u]) continue;
vis[u] = 1;
for(auto &[v, w] : g[u]) {
if(d[v] > d[u] + w) {
d[v] = d[u] + w;
Q.push({d[v], v});
}
}
}
return d;
};
auto d1 = dijkstra(s1, G);
auto d2 = dijkstra(s2, G);
auto d3 = dijkstra(t, rG);
LL ans = INF;
for(int i = 0; i < n; ++ i)
ans = min(ans, d1[i] + d2[i] + d3[i]);
cout << (ans >= INF ? -1 : ans) << "\n";
return 0;
}
D题
大意:一棵树 取一个节点就要把子树全部取掉 求取不超过m个节点的最大值
一道非常典型的树上背包问题啊!!!!当时脑子抽了 居然没打出来 好气啊!!!
dp[i,j] 表示以i为根的子树 保留连续的j个最小值
因为要保证连续 所以先设初始值 dp[i,1]=val[i] 这样后面转移保留比1大的数量时候一定都会加上i节点本身
code:
#include<bits/stdc++.h>
using namespace std;
#define lowbit(x) x&(-x)
#define ll long long
const int maxn=505;
ll dp[maxn][maxn],val[maxn],sz[maxn],sum;
vector<int>Q[maxn];
ll n,m;
void dfs(int,int);
void dfs2(int,int);
int main(){
memset(dp,0x3f,sizeof(dp));
cin>>n>>m;
for(int i=1;i<n;i++){
int aa,bb;
scanf("%d%d",&aa,&bb);
Q[aa].push_back(bb);
Q[bb].push_back(aa);
}
for(int i=1;i<=n;i++)scanf("%lld",&val[i]),sum+=val[i];
dfs(1,1);
dfs2(1,1);
if(m>=n)m=n-1;
cout<<sum-dp[1][n-m]<<endl;
return 0;
}
void dfs(int u,int fa){
sz[u]=1;
for(int i=0;i<Q[u].size();i++){
int to=Q[u][i];
if(to==fa)continue;
dfs(to,u);
sz[u]+=sz[to];
}
}
void dfs2(int u,int fa){
dp[u][1]=val[u];
for(int i=0;i<Q[u].size();i++){
int to=Q[u][i];
if(to==fa)continue;
dfs2(to,u);
for(ll S=n-m;S>0;S--)
for(int k=0;k<=min(sz[to],S);k++)
dp[u][S]=min(dp[to][k]+dp[u][S-k],dp[u][S]);
}
}
E题
大意:一个序列 给定一个最大值max 最小值min 求有多少子序列满足最大值为max 最小值为min
分析:这应该算一个非常经典的问题
考虑容斥 设函数 calc(l,r) 统计都数值都在 内的区间个数。
calc函数还是很好算 直接线性扫一遍即可
经典容斥 :ans=calc(l,r)- calc(l+1,r) - calc(l,r-1) +calc(l+1,r-1)
code:
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
const int N = 200010;
LL n, a[N];
LL ans;
void solve(LL x, LL y, LL t) {
int lst = 1;
for(int i = 1; i <= n; ++ i) {
if(a[i] > x or a[i] < y) {
lst = i + 1;
}
ans += (i - lst + 1) * t;
}
}
int main() {
ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
LL x, y;
cin >> n >> x >> y;
for(int i = 1; i <= n; ++ i) cin >> a[i];
solve(x, y, 1);
solve(x - 1, y, -1);
solve(x, y + 1, -1);
solve(x - 1, y + 1, 1);
cout << ans << "\n";
return 0;
}
G题
大意:一个字符串 求每个子序列的值 定义一个子序列的值为不同字符的个数
分析:
也是一道非常经典的计数问题
很明显考虑每个字符单独的贡献为多少
贡献:
左边界在上一个相同的该字母之后,在它之前(左开右闭)
右边界在它之后(闭区间)
和之前有个算有多少个子序列乘积为0一模一样!!!!!!!!!!!!
但是当时比赛就是没想出来唉 这是一个模型 一定要记住!!!!
这样只需要记录一下last数组就好了
code:
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
const int N = 200010;
LL n, a[N];
LL ans;
void solve(LL x, LL y, LL t) {
int lst = 1;
for(int i = 1; i <= n; ++ i) {
if(a[i] > x or a[i] < y) {
lst = i + 1;
}
ans += (i - lst + 1) * t;
}
}
int main() {
ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
LL x, y;
cin >> n >> x >> y;
for(int i = 1; i <= n; ++ i) cin >> a[i];
solve(x, y, 1);
solve(x - 1, y, -1);
solve(x, y + 1, -1);
solve(x - 1, y + 1, 1);
cout << ans << "\n";
return 0;
}
H题
大意:一个01矩阵 对角线反转 行反转 列反转 问能否经过一系列操作将矩阵全部转化为0 并记录过程
分析:
首先不需要最优化步骤
每行或者每列要么反转一次 要么不反转
对角线特殊判断一下
默认第一行不反转,那么每一个1所在的列一定是翻转了。检查每一行是否能够不导致冲突。
有方案的话把记录下来的内容输出。
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
int T;
cin >> T;
while(T -- ) {
int n;
cin >> n;
vector<vector<int>> A(n, vector<int> (n));
for(int i = 0; i < n; ++ i)
for(int j = 0; j < n; ++ j)
cin >> A[i][j];
vector<int> row, col;
auto solve = [&]() -> bool {
row.clear();
col.clear();
for(int i = 1; i < n; ++ i) {
int c = 0;
for(int j = 0; j < n; ++ j)
c += (A[i][j] ^ A[0][j]);
if(c > 0 and c < n) return 0;
if(c == n) row.push_back(i);
}
for(int i = 0; i < n; ++ i)
if(A[0][i]) col.push_back(i);
return 1;
};
if(solve()) {
cout << "YES\n";
cout << row.size() + col.size() << "\n";
for(int &i : row) cout << "row " << i + 1 << "\n";
for(int &i : col) cout << "col " << i + 1 << "\n";
continue;
};
for(int i = 0; i < n; ++ i) A[i][i] ^= 1;
if(solve()) {
cout << "YES\n";
cout << row.size() + col.size() + 1 << "\n";
cout << "diagonal\n";
for(int &i : row) cout << "row " << i + 1 << "\n";
for(int &i : col) cout << "col " << i + 1 << "\n";
continue;
};
cout << "NO\n";
}
return 0;
}
K题:打表找规律
L题:模拟多项式除法