一题多解

二维DP转图论

P1854 [IOI 1999] 花店橱窗布置

dp

#include <bits/stdc++.h>

constexpr int inf = 1E9;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int f, v;
    std::cin >> f >> v;

    std::vector a(f, std::vector<int>(v));
    for (int i = 0; i < f; ++i) {
        for (int j = 0; j < v; ++j) {
            std::cin >> a[i][j];
        }
    }

    std::vector dp(f + 1, std::vector<int>(v + 1, -inf));
    
    dp[0].assign(v + 1, 0);
    for (int i = 0; i < f; ++i) {
        for (int j = i; j < v; ++j) {
            dp[i + 1][j + 1] = std::max(dp[i + 1][j], dp[i][j] + a[i][j]);
        }
    }

    std::cout << dp[f].back() << "\n";

    std::vector<int> ans(f);
    for (int i = f, j = v; i >= 0; --i) {
        while (j > 0 && dp[i][j] == dp[i][j - 1]) {
            --j;
        }
        ans[i - 1] = j--;
    }

    for (int x = 0; x < ans.size(); ++x) {
        std::cout << ans[x] << " \n"[x + 1 == ans.size()];
    }
}

dijkstra / spfa最长路

//楼下全是dp,那么来个最长路做法 
//可以将这个看成一个以花为横,瓶为纵的表格从下向上找一条路径,上一排的位置必须小于下一排
//那么我们就可以连边去跑最长路了
//同时我们注意到需要记录路径
//那么这里就选dij好了(因为其他的不会记路径啊) 
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#pragma GCC optimize(3)
#define re register
#define maxn 10001
#define maxw 800000
#define inf -99999999
using namespace std;
struct node
{
    int v,w,nxt;
}e[maxw];
int f,v,ans,num=1,end;
int head[maxn],d[maxn],r[maxn];
int a[101][101];
typedef pair<int,int> pii;
priority_queue<pii,vector<pii>,less<pii> > q;
inline int read()
{
    char c=getchar();
    int x=0;
    int r=1;
    while(c<'0'||c>'9') 
    {
        if(c=='-') r=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9')
    {
        x=x*10+c-48;
        c=getchar();
    }
    return r*x;
}
inline void add_edge(int x,int y,int z)
{
    e[num].v=y;
    e[num].w=z;
    e[num].nxt=head[x];
    head[x]=num++;
}
inline void dijkstra(int s)
{
    for(re int i=1;i<=f*v;i++)
      d[i]=inf;
    d[s]=0;
    q.push(make_pair(d[s],s));
    while(q.size())
    {
        pii mid=q.top();
        q.pop();
        int k=mid.second;
        for(re int i=head[k];i;i=e[i].nxt)
        if(d[e[i].v]<d[k]+e[i].w)
        {
            d[e[i].v]=d[k]+e[i].w;
            q.push(make_pair(d[e[i].v],e[i].v));//很常规的松弛操作 
            r[e[i].v]=k;//存一下前驱结点,用来找路径 
        }
    }
}
void dfs(int i)
{
    if(i!=0) dfs(r[i]);
    if(i!=0) cout<<i%v<<" ";//dfs找出路径,再将点还原成二维 
}
int main()
{
    f=read();
    v=read();
    for(re int i=1;i<=f;i++)
    for(re int j=1;j<=v;j++)
    a[i][j]=read();
    for(re int i=1;i<=v;i++)
      add_edge(0,i,a[1][i]);//我们把0作为起点,把第一列的所有点与0相连 
    for(re int i=2;i<=f;i++)
    for(re int k=1;k<=v;k++)
    for(re int j=k+1;j<=v;j++)
      add_edge((i-2)*v+k,(i-1)*v+j,a[i][j]);//把点的坐标压成一维 
      //连边,需要注意的是上一行点的横坐标要小于下一行的 
    dijkstra(0);
    ans=inf;
    for(re int i=1;i<=v;i++)
    if(ans<d[(f-1)*v+i])//从最后一行找最长路 
    {
        ans=d[(f-1)*v+i];
        end=(f-1)*v+i;
    }
    cout<<ans<<endl;
    dfs(r[end]);
    end%=v;
    if(end==0) end=v;
    cout<<end<<endl;
    return 0;
}

最小费用最大流

#include<bits/stdc++.h>
using namespace std;
struct Edge
{
    int to,nxt,flow,cost;
}edge[500005];
int fir[100005],cnt=1;
void add(int u,int v,int w,int c)
{
    edge[++cnt]=(Edge){v,fir[u],w,-c},fir[u]=cnt;
    edge[++cnt]=(Edge){u,fir[v],0,c},fir[v]=cnt;
}
int dis[100005],vis[100005];
int s,t;
int n,m;
bool spfa()
{
    memset(dis,0x3f,sizeof(dis));
    memset(vis,0,sizeof(vis));
    queue<int>q;
    q.push(s);
    dis[s]=0;
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        vis[u]=0;
        for(int i=fir[u];i;i=edge[i].nxt)
        {
            int v=edge[i].to;
            if(dis[v]>dis[u]+edge[i].cost&&edge[i].flow)
            {
                dis[v]=dis[u]+edge[i].cost;
                if(!vis[v])q.push(v),vis[v]=1;
            }
        }
    } 
    return dis[t]<0x3f3f3f3f;
}
int MC,MF;
int dfs(int u,int flow)
{
    if(u==t)
    {
        MF+=flow;
        vis[u]=1;
        return flow;
    }
    int used=0;
    vis[u]=1;
    for(int i=fir[u];i;i=edge[i].nxt)
    {
        int v=edge[i].to;
        if((v==t||!vis[v])&&edge[i].flow&&dis[v]==dis[u]+edge[i].cost)
        {
            int mi=dfs(v,min(flow-used,edge[i].flow));
            if(mi)
            {
                edge[i].flow-=mi;
                edge[i^1].flow+=mi;
                used+=mi;
                MC+=mi*edge[i].cost;
            }
            if(used==flow)return used;
        }
    }
    return used;
}
void MCMF()
{
    while(spfa())
    {
        vis[t]=1;
        while(vis[t])memset(vis,0,sizeof(vis)),dfs(s,1);
    }
}
int main()
{
    s=0,t=50000;
    int s2=50001;
    add(s,s2,1,0);
    scanf("%d%d",&n,&m);
    int sum=n*m;
    for(int i=1;i<=n;++i)
    {
        for(int j=1;j<=m;++j)
        {
            int k;
            scanf("%d",&k);
            add((i-1)*m+j,(i-1)*m+j+sum,1,k);
        }
    }
    for(int i=1;i<n;++i)
    {
        for(int j=1;j<m;++j)
        {
            for(int k=j+1;k<=m;++k)
            {
                add((i-1)*m+j+sum,i*m+k,1,0);
            }
        }
    }
    for(int i=1;i<=m;++i)
    {
        add(s2,i,1,0);
        add((n-1)*m+i+sum,t,1,0);
    }
    MCMF();
    printf("%d\n",-MC);
    for(int i=1;i<=n;++i)
    {
        for(int j=1;j<=m;++j)
        {
            for(int k=fir[(i-1)*m+j];k;k=edge[k].nxt)
            {
                int v=edge[k].to;
                if(v-((i-1)*m+j)==sum&&edge[k].flow==0)
                {
                    printf("%d ",j);
                }
            }
        }
    }
}

P1073 [NOIP 2009 提高组] 最优贸易

P1073 [NOIP 2009 提高组] 最优贸易

SCC

#include <bits/stdc++.h>

using i64 = long long;

struct SCC {
    int n;
    std::vector<std::vector<int>> adj;
    std::vector<int> stk;
    std::vector<int> dfn, low, bel;
    int cur, cnt;
    
    SCC() {}
    SCC(int n) {
        init(n);
    }
    
    void init(int n) {
        this->n = n;
        adj.assign(n, {});
        dfn.assign(n, -1);
        low.resize(n);
        bel.assign(n, -1);
        stk.clear();
        cur = cnt = 0;
    }
    
    void addEdge(int u, int v) {
        adj[u].push_back(v);
    }
    
    void dfs(int x) {
        dfn[x] = low[x] = cur++;
        stk.push_back(x);
        
        for (auto y : adj[x]) {
            if (dfn[y] == -1) {
                dfs(y);
                low[x] = std::min(low[x], low[y]);
            } else if (bel[y] == -1) {
                low[x] = std::min(low[x], dfn[y]);
            }
        }
        
        if (dfn[x] == low[x]) {
            int y;
            do {
                y = stk.back();
                bel[y] = cnt;
                stk.pop_back();
            } while (y != x);
            cnt++;
        }
    }
    
    std::vector<int> work() {
        // for (int i = 0; i < n; i++) {
        //     if (dfn[i] == -1) {
        //         dfs(i);
        //     }
        // }
        dfs(0);
        return bel;
    }
};

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int n, m;
    std::cin >> n >> m;

    std::vector<int> a(n);
    for (int i = 0; i < n; ++i) {
        std::cin >> a[i];
    }

    SCC g(n);

    for (int i = 0, u, v, t; i < m; ++i) {
        std::cin >> u >> v >> t;
        --u, --v;
        g.addEdge(u, v);
        if (t == 2) {
            g.addEdge(v, u);
        }
    }

    g.work();

    int N = g.cnt;

    constexpr int inf = 1E9;
    std::vector<int> f0(N, inf), f1(N);
    for (int i = 0; i < n; ++i) {
        int j = g.bel[i];
        if (j == -1) {
            continue;
        }
        f0[j] = std::min(f0[j], a[i]);
        f1[j] = std::max(f1[j], a[i]);
    }

    std::vector<std::vector<int>> adj(N);
    std::vector<int> d(N);

    for (int i = 0; i < n; ++i) {
        for (auto v : g.adj[i]) {
            int x = g.bel[i], y = g.bel[v];
            if (x == y || x == -1 || y == -1) {
                continue;
            }
            adj[x].push_back(y);
            ++d[y];
        }
    }
    
    std::vector<int> dp(N);

    std::queue<int> q;
    q.push(g.bel[0]);

    int ans = 0;
    while (!q.empty()) {
        auto u = q.front();
        q.pop();
        
        dp[u] = std::max(dp[u], f1[u] - f0[u]);

        for (auto v : adj[u]) {
            f0[v] = std::min(f0[v], f0[u]);
            dp[v] = std::max(dp[u], dp[v]);
            if (--d[v] == 0) {
                q.push(v);
            }
        }
    }
    
    std::cout << dp[g.bel[n - 1]] << "\n";
}

分层图

image

#include <bits/stdc++.h>

using i64 = long long;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int n, m;
    std::cin >> n >> m;

    std::vector<int> a(n);
    for (int i = 0; i < n; ++i) {
        std::cin >> a[i];
    }

    std::vector<std::vector<std::pair<int, int>>> adj(3 * n);

    auto addEdge = [&](int u, int v) {
        adj[u].push_back({v, 0});
        adj[u + n].push_back({v + n, 0});
        adj[u + 2 * n].push_back({v + 2 * n, 0});
    };

    for (int i = 0, u, v, t; i < m; ++i) {
        std::cin >> u >> v >> t;
        --u, --v;
        addEdge(u, v);
        if (t == 2) {
            addEdge(v, u);
        }
    }

    for (int u = 0; u < n; ++u) {
        adj[u].push_back({u + n, -a[u]});
        adj[u + n].push_back({u + 2 * n, a[u]});
    }

    constexpr int inf = 1E9;
    std::vector<int> dist(3 * n, -inf);
    auto spfa = [&]() -> void {
        std::vector<bool> st(3 * n);
        std::queue<int> q;
        q.push(0);
        st[0] = true;
        dist[0] = 0;

        while (!q.empty()) {
            auto u = q.front();
            q.pop();
    
            st[u] = false;
        
            for (auto [v, w] : adj[u]) {
                if (dist[v] >= dist[u] + w) {
                    continue;
                }
                dist[v] = dist[u] + w;
                if (!st[v]) {
                    st[v] = true;
                    q.push(v);
                }
            }
        }
    };
    spfa();

    std::cout << std::max(dist.back(), 0) << "\n";
}s

反图

后面最大 - 前面最小
两次SPFA

#include <bits/stdc++.h>

using i64 = long long;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int n, m;
    std::cin >> n >> m;

    std::vector<int> a(n);
    for (int i = 0; i < n; ++i) {
        std::cin >> a[i];
    }

    std::vector<std::vector<int>> g(n), rg(n);
    for (int i = 0, u, v, t; i < m; ++i) {
        std::cin >> u >> v >> t;
        --u, --v;
        g[u].push_back(v);
        rg[v].push_back(u);
        if (t == 2) {
            g[v].push_back(u);
            rg[u].push_back(v);
        }
    }

    constexpr int inf = 1E9;

    std::vector<int> mn(n, inf);
    {
        std::queue<int> q;
        std::vector<bool> inq(n, false);
        mn[0] = a[0];
        q.push(0);
        inq[0] = true;

        while (!q.empty()) {
            int u = q.front();
            q.pop();
            inq[u] = false;

            for (int v : g[u]) {
                if (mn[v] > std::min(mn[u], a[v])) {
                    mn[v] = std::min(mn[u], a[v]);
                    if (!inq[v]) {
                        inq[v] = true;
                        q.push(v);
                    }
                }
            }
        }
    }

    std::vector<int> mx(n, -inf);
    {
        std::queue<int> q;
        std::vector<bool> inq(n, false);
        mx[n - 1] = a[n - 1];
        q.push(n - 1);
        inq[n - 1] = true;

        while (!q.empty()) {
            int u = q.front();
            q.pop();
            inq[u] = false;

            for (int v : rg[u]) {
                if (mx[v] < std::max(mx[u], a[v])) {
                    mx[v] = std::max(mx[u], a[v]);
                    if (!inq[v]) {
                        inq[v] = true;
                        q.push(v);
                    }
                }
            }
        }
    }

    int ans = 0;
    for (int i = 0; i < n; ++i) {
        if (mn[i] == inf || mx[i] == -inf) {
            continue;
        }
        ans = std::max(ans, mx[i] - mn[i]);
    }

    std::cout << ans << '\n';
    return 0;
}

XOR 问题

数组A全部xor 一个数,使得最大值最小
问题

Trie

#include <bits/stdc++.h>

using i64 = long long;

constexpr int N = 1E7;

int tot;
int trie[N][2];

int newNode() {
    ++tot;
    trie[tot][0] = trie[tot][1] = 0;
    return tot;
}

void insert(int x) {
    int p = 1;
    for (int i = 30; i >= 0; --i) {
        int u = x >> i & 1;
        if (!trie[p][u]) {
            trie[p][u] = newNode();
        }
        p = trie[p][u];
    }
}


int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int n;
    std::cin >> n;

    std::vector<int> a(n);
    for (int i = 0; i < n; ++i) {
        std::cin >> a[i];
    }

    tot = 0;
    newNode();

    for (int x : a) {
        insert(x);
    }

    auto dfs = [&](this auto &&self, int p, int k) -> int {
        if (p == 0) {
            return 0;
        }
        if (trie[p][0] && trie[p][1]) {
            return std::min(self(trie[p][0], k - 1), self(trie[p][1], k - 1)) | 1 << k;
        }
        return self(std::max(trie[p][0], trie[p][1]), k - 1);
    };
    
    std::cout << dfs(1, 30);
}

sort + partition_point

#include <bits/stdc++.h>

using i64 = long long;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    int N;
    std::cin >> N;
    
    std::vector<int> a(N);
    for (int i = 0; i < N; i++) {
        std::cin >> a[i];
    }
    
    std::sort(a.begin(), a.end());
    
    auto solve = [&](auto solve, int l, int r, int k) {
        if (k == 0) return 0;
        int m = std::partition_point(a.begin() + l, a.begin() + r, [&](int x) { return ~x >> (k - 1) & 1; }) - a.begin();
        if (l == m || m == r) return solve(solve, l, r, k - 1);
        return std::min(solve(solve, l, m, k - 1), solve(solve, m, r, k - 1)) | 1 << (k - 1);
    };
    
    std::cout << solve(solve, 0, N, 30) << "\n";
    
    return 0;
}

数组中 $ A_i + A_j = C$ 的个数

题目

二分

#include <bits/stdc++.h>

using i64 = long long;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int n, c;
    std::cin >> n >> c;

    std::vector<int> a(n);
    for (int i = 0; i < n; ++i) {
        std::cin >> a[i];
    }

    std::sort(a.begin(), a.end());

    i64 ans = 0;
    for (int i = 0; i < n; ++i) {
        int x = a[i] - c;
        ans += std::upper_bound(a.begin(), a.end(), x) - std::lower_bound(a.begin(), a.end(), x);
    }

    std::cout << ans << "\n";
}

双指针

#include <bits/stdc++.h>

using i64 = long long;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int n, c;
    std::cin >> n >> c;

    std::vector<int> a(n);
    for (int i = 0; i < n; ++i) {
        std::cin >> a[i];
    }

    std::sort(a.begin(), a.end());

    i64 ans = 0;
    for (int i = 0, l = 0, r = 0; i < n; ++i) {
        int x = a[i] - c;
        while (a[l] < x) {
            ++l;
        }
        while (a[r] <= x) {
            ++r;
        }
        ans += r - l;
    }

    std::cout << ans << "\n";
}

hash表

#include <bits/stdc++.h>

using i64 = long long;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    i64 n, c;
    std::cin >> n >> c;

    std::vector<i64> v(n);
    for (int i = 0; i < n; ++i) {
        std::cin >> v[i];
    }

    std::unordered_map<i64, int> h;
    i64 re = 0;
    for (i64 x : v) {
        re += h[x - c] + h[c + x];
        ++h[x];
    }
    std::cout << re;

    return 0;   // 结束了罪恶的一生!
}   

仓库选址

104. 货仓选址

数轴上n个人的坐标,问走的一起的总路程最小是多少?

#include <bits/stdc++.h>

using i64 = long long;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    int n;
    std::cin >> n;

    std::vector<int> a(n);
    for (int i = 0; i < n; ++i) {
        std::cin >> a[i];
    }

    int m = n / 2;
    std::sort(a.begin(), a.end());
    
    i64 ans = 0;
    for (int x : a) {
        ans += std::abs(x - a[m]);
    }
    std::cout << ans << "\n";
}
#include <bits/stdc++.h>

using i64 = long long;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    int n;
    std::cin >> n;

    std::vector<int> a(n);
    for (int i = 0; i < n; ++i) {
        std::cin >> a[i];
    }

    std::sort(a.begin(), a.end());
    
    i64 ans = 0;
    for (int i = 0; i < n; ++i) {
        ans += a[i] - a[i / 2];
    }
    std::cout << ans << "\n";
}
#include <bits/stdc++.h>

using i64 = long long;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    int n;
    std::cin >> n;

    std::vector<int> a(n);
    for (int i = 0; i < n; ++i) {
        std::cin >> a[i];
    }

    int m = n / 2;
    std::nth_element(a.begin(), a.begin() + m, a.end());
    
    i64 ans = 0;
    for (int x : a) {
        ans += std::abs(x - a[m]);
    }
    std::cout << ans << "\n";
}
#include <bits/stdc++.h>

using i64 = long long;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    int n;
    std::cin >> n;

    constexpr int N = 4E4 + 5;
    std::vector<int> cnt(N);
    for (int i = 0, x; i < n; ++i) {
        std::cin >> x;
        ++cnt[x];
    }

    i64 ans = 0;
    for (int i = 0, s = 0; i < N; ++i) {
        s += cnt[i];
        ans += std::min(s, n - s);
    }
    std::cout << ans << "\n";
}

倍增可以 按位和二分查询

题目
l区间and >= k 的最右侧 r

#include <bits/stdc++.h>

using i64 = long long;

void solve() {
    int n;
    std::cin >> n;

    std::vector<int> a(n);
    for (int i = 0; i < n; ++i) {
        std::cin >> a[i];
    }

    int L = std::__lg(n);
    std::vector val(L + 1, std::vector<int>(n));
    
    val[0] = a;
    for (int i = 0; i < L; ++i) {
        for (int j = 0; j + (1 << i) < n; ++j) {
            val[i + 1][j] = val[i][j] & val[i][j + (1 << i)];
        }
    }

    auto qry = [&](int l, int r) -> int {
        int len = std::__lg(r - l);
        return val[len][l] & val[len][r - (1 << len)];
    };

    int q;
    std::cin >> q;

    for (int i = 0; i < q; ++i) {
        int l, k;
        std::cin >> l >> k;
        --l;
        
        int lo = l, hi = n;
        while (lo < hi) {
            auto mid = lo + (hi - lo + 1) / 2;
            if (qry(l, mid) >= k) {
                lo = mid;
            } else {
                hi = mid - 1;
            }
        }
        std::cout << (lo == l ? -1 : lo) << " ";

        // int x = (1 << 30) - 1;
        // int p = l;
        // for (int j = L; j >= 0 && p < n; --j) {
        //     if ((val[j][p] & x) >= k) {
        //         x &= val[j][p];
        //         p += 1 << j;
        //     }
        // }
        // std::cout << (p == l ? -1 : p) << " ";
    }
    std::cout << std::endl;
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int t;
    std::cin >> t;

    while (t--) {
        solve();
    }
}

二分图判断 DSU

博客有一场CF E题
Codeforces Round 805 (Div. 3)

posted @ 2026-03-13 15:29  filt  阅读(6)  评论(0)    收藏  举报