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';
    }
}
posted @ 2026-07-29 11:01  ricky_lin  阅读(14)  评论(0)    收藏  举报