2026.07.28 HDU 多校ACM
1003 Best
tag: DP
我们用一个数组 \(a_i\) 表示维护只考虑前 \(k\) 个数,子序列长度为 \(i\) 前缀和最小值即可
code
#include<bits/stdc++.h>
using namespace std;
const int NN = 1e5 + 8;
typedef long long ll;
int f[NN];
ll a[NN];
vector<ll> s;
void solve(){
s.resize(0);
int n;
cin >> n;
s.resize(70);
for(int i = 1; i < 65; ++i) s[i] = 2e18;
for(int i = 1; i <= n; ++i){
cin >> a[i];
for(int j = 64; j >= 1; --j){
if(s[j-1] <= a[i]) s[j] = min(s[j],s[j-1]+a[i]);
}
}
ll ans = 0;
for(int i = 0; i < 65; ++i) if(s[i] != 2e18) ans = i;
cout << ans << '\n';
}
int main(){
ios::sync_with_stdio(false),cin.tie(0);
int T;
cin >> T;
while(T--){
solve();
}
}
1004 toys
tag: 网络流
可以发现是一个最小费用最大流喵
源点向小朋友点连 1 流量
小朋友点向自己喜欢的玩具连 1 流量
玩具向汇点连多条 1 流量的边,费用为第 \(i\) 次购买需要花的钱
最后跑个最小费用最大流就可以啦
code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll INF = 1e18;
struct Edge{
int to, next, cap;
ll cost;
};
vector<vector<Edge>> g;
vector<ll> h, dist;
vector<int> prevv, preve;
void add_edge(int u, int v, int cap, ll cost){
g[u].push_back({v, (int)g[v].size(), cap, cost});
g[v].push_back({u, (int)g[u].size() - 1, 0, -cost});
}
ll min_cost_flow(int s, int t, int maxf){
int V = g.size();
h.assign(V, 0);
dist.assign(V, 0);
prevv.assign(V, 0);
preve.assign(V, 0);
ll res = 0;
int flow = 0;
while(flow < maxf){
fill(dist.begin(), dist.end(), INF);
dist[s] = 0;
priority_queue<pair<ll, int>, vector<pair<ll, int>>, greater<pair<ll, int>>> pq;
pq.push({0, s});
while(!pq.empty()){
auto [d, v] = pq.top(); pq.pop();
if(dist[v] < d) continue;
for(int i = 0; i < (int)g[v].size(); ++i){
Edge &e = g[v][i];
if(e.cap > 0){
ll nd = d + e.cost + h[v] - h[e.to];
if(nd < dist[e.to]){
dist[e.to] = nd;
prevv[e.to] = v;
preve[e.to] = i;
pq.push({nd, e.to});
}
}
}
}
if(dist[t] == INF) break; // 无增广路(理论上不会发生)
for(int v = 0; v < V; v++){
if (dist[v] < INF) h[v] += dist[v];
}
int d = maxf - flow;
for(int v = t; v != s; v = prevv[v]){
d = min(d, g[prevv[v]][preve[v]].cap);
}
flow += d;
ll path_cost = 0;
for(int v = t; v != s; v = prevv[v]){
Edge &e = g[prevv[v]][preve[v]];
path_cost += e.cost;
e.cap -= d;
g[v][e.next].cap += d;
}
res += path_cost * d;
}
return res;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin >> T;
while(T--){
int n, m;
cin >> n >> m;
int S = 0;
int child_start = 1;
int toy_start = 1 + n;
int Tnode = 1 + n + m;
int V = Tnode + 1;
g.assign(V, {});
// 源点到小朋友
for(int i = 0; i < n; ++i){
add_edge(S, child_start + i, 1, 0);
}
// 小朋友到玩具
for(int i = 0; i < n; ++i){
int x;
cin >> x;
while(x--){
int toy;
cin >> toy;
// toy 编号 1..m, 节点号 toy_start + (toy-1)
add_edge(child_start + i, toy_start + (toy - 1), 1, 0);
}
}
// 玩具到汇点
for(int j = 0; j < m; j++){
int y;
cin >> y;
for(int k = 0; k < y; k++){
ll w;
cin >> w;
add_edge(toy_start + j, Tnode, 1, w);
}
}
ll ans = min_cost_flow(S, Tnode, n);
cout << ans << '\n';
}
return 0;
}
1005 GCD
tag: 数论
这道题需要求出最大的质因子次数 \(k\),答案就是 \(\lfloor \log k \rfloor + 1\)
我们考虑我们只需要对于每个数验证 \(1\sim 10^6\) 的质数
如果都不能整除,那么剩下的数只有是完全平方数最后的答案才能是 \(2\) 否则是 \(1\),因为如果是 \(a\times b^2\) 最后乘起来的答案一定会大于 \(10^{18}\)
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll NN = 1e6 + 8;
int prime[NN],cnt;
bool vis[NN];
void init(){
for(int i = 2; i <= 1e6; ++i){
if(!vis[i]) prime[++cnt] = i;
for(int j = 2; j <= cnt && i*prime[j] <= 1e6; ++j){
vis[i*prime[j]] = 1;
if(i%prime[j] == 0) break;
}
}
}
ll calc(ll x){
ll ans = 0;
for(int i = 1; i <= cnt; ++i){
ll res = 0;
ll p = prime[i]*prime[i];
while(x % p == 0){x/=p;res+=2;}
if(x % prime[i] == 0) x /= prime[i],++res;
ans = max(ll(log2(res)),ans);
}
ll sq = sqrt(x);
if(sq != 1 && sq * sq == x) ans = max(ans,1ll);
return ans+1;
}
void solve(){
ll x;
cin >> x;
cout << calc(x) << '\n';
}
int main(){
init();
ios::sync_with_stdio(false),cin.tie(0);
int T;
cin >> T;
while(T--){
solve();
}
}
1006 Special Judge
tag: 数论 Dilworth
我们考虑如果不考虑字典序最小,那么显然答案就可以是 \(\frac n 2 \sim n\)
我们手模 \(1\sim 20\) 可以得到
11 12 13 14 15 16 17 18 19 20
可以变成
11 6 13 7 15 8 17 9 19 10
我们考虑可以将所有数分组成 \(a\times 2^b(a~mod 2 == 1)\),显然我们让所有的 \(b\) 顶格就可以得到满足条件的解
怎么得到最优解呢?
只需要满足对于所有的 \(a_2|a_1\),包含 \(a_1\times 2^{b_1}\) 和 \(a_2\times 2^{b_2}\) 的方案中 \(b_1 > b_2\)
这可以通过从小到大判断是否有包含这个数的方案,验证的时候从大到小即可
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int NN = 2e3 + 8;
int odd[NN],val[NN];
bool vis[NN];
void solve(){
int n;
cin >> n;
for(int i = 1; i <= n; i += 2) odd[(i+1)/2] = i;
for(int i = 1; i <= n; ++i) val[i] = 0,vis[i] = 0;
int m = (n+1)/2;
vector<int> H(n+1,0);
for(ll i = 1; i <= m; ++i){
ll x = odd[i];
while((x << (H[x]+1)) <= n) ++H[x];
}
vector<vector<int>> succ(n + 1);
for(int x = 1; x <= n; x += 2)
for(int y = x+2; y <= n; y += 2){
if(y % x == 0)
succ[x].push_back(y);//鎵惧鏁伴噷浜掔浉鏁撮櫎鐨勬暟
}
vector<int> ans;
auto check = [&]() -> bool {
vector<int> low(n + 1);
for(int o = 1; o <= n; o += 2) low[o] = val[o];
// 浠庡ぇ鍒板皬鏇存柊锛屼繚璇佸悗缁у凡澶勭悊
for(int idx = m; idx > 0; --idx){
int o = odd[idx];
for(int v : succ[o]) low[o] = max(low[o], low[v] + 1);
}
for(int i = 1; i <= m; ++i){
int o = odd[i];
if(vis[o] && low[o] != val[o]) return false;
if(low[o] > H[o]) return false;
}
return true;
};
for(int i = 1; i <= n; ++i){
int x = i, pw = 0;
while(!(x&1)){
x /= 2;
++pw;
}
if(vis[x]) continue;
vis[x] = true;
val[x] = pw;
if(check()) ans.push_back(i);
else{
vis[x] = false;
val[x] = pw+1;
}
}
cout << ans.size() << "\n";
for(auto x : ans){
cout << x << " ";
}
cout << "\n";
}
int main(){
ios::sync_with_stdio(false),cin.tie(0);
int T;
cin >> T;
while(T--){
solve();
}
}
1009 Six Grade
tag: 期望 基环树
一开始想的是二分图最大匹配,但是显然时间复杂度不够
但是有好处是左部点的度为 \(2\),但是没什么用
我们继续研究性质,可以发现,将题目作为边,答案作为点,连出来的图可以构成一个基环树森林,所以说在基环树环上的边的答案不确定,但是下面树边对应的题目的答案是一定可以确定的
所以答案为 \(总点数 - \frac {环上点数} 2\)
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MOD = 998244353, inv2 = (MOD + 1) / 2;
const int NN = 1e6 + 8;
ll cnt_r;
int deg[NN];
vector<int> e[NN];
queue<int> q;
void solve(){
ll n;
cin >> n;
cnt_r = 0;
for(int i = 1; i <= n; ++i) deg[i] = 0,e[i].resize(0);
for(int i = 1,u,v; i <= n; ++i){
cin >> u >> v;
++deg[u];++deg[v];
e[u].push_back(v);e[v].push_back(u);
}
for(int i = 1; i <= n; ++i){
// cout << deg[i] << " ";
if(deg[i] == 1) q.push(i);
}
while(!q.empty()){
++cnt_r;
int u = q.front();q.pop();
--deg[u];
for(auto v : e[u]){
if(deg[v]){
--deg[v];
if(deg[v] == 1) q.push(v);
}
}
}
// cout << cnt_r << endl;
cout << (n+cnt_r) * inv2 % MOD << "\n";
}
int main(){
ios::sync_with_stdio(false),cin.tie(0);
int T;
cin >> T;
while(T--){
solve();
}
}
1010 Random
tag: 期望
答案是 \(w^l+l-1\) 喵,但是现在不太会推喵
简单理解就是长度为 \(l\) 的曲子一共有 \(w^l\) 种,所以每次有 \(\frac 1 {w^l}\) 的概率成功,期望 \(w^l\) 次,所以期望长度 \(w^l+l-1\)
严格证明的话就是,我们假设喜欢的歌固定,并且 border 长度为 \(k\),那么期望应该就是 \(\sum_{i=1}^k w^i\) 了,border>=k 字符串的数量为 \(w^{l-k}\)
所以总的长度为 \(sum = \sum_{i=1}^{l-1} w^i \times w^{l-i} + w^{2l} = w^l(w^l+l-1)\)
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MOD = 998244353;
ll ksm(ll x,ll k){
ll res = 1;
while(k){
if(k&1) res = res * x % MOD;
x = x * x % MOD;
k >>= 1;
}
return res;
}
int main(){
int T;
ios::sync_with_stdio(false),cin.tie(0);
cin >> T;
while(T--){
ll w,l;
cin >> w >> l;
cout << (ksm(w,l)+l-1) % MOD << '\n';
}
}
本文来自博客园,作者:ricky_lin,转载请注明原文链接:https://www.cnblogs.com/rickylin/p/22025818

浙公网安备 33010602011771号