杭电多校第一场补题

  • 1005.Path

http://acm.hdu.edu.cn/showproblem.php?pid=6582

题目描述:给你一个图,问你将所有最短路都断开的最小花费是多少?将一条边断开的花费等于他的长度。

很经典的最短路去边加网络流的题目,首先跑一遍最短路,将不是最短路上的路径全部去掉,再在新图上跑一遍最大流,最小割就等于最大流。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cmath>
#include <queue>
#include <vector>
#include <map>
#define int long long
using namespace std;
const int maxn=1e4+10;
const int INF=0x3f3f3f3f3f3f3f3f;
struct Edge{
    int from,to,w;
    Edge(int u,int v,int w):from(u),to(v),w(w){}
};
struct HeapNode{
    int d,u;
    HeapNode(int _d,int _u):d(_d),u(_u){}
    bool operator < (const HeapNode &rhs)const{
        return d>rhs.d;
    }
};

struct Dijkstra{

    vector<int>G[maxn];
    vector<Edge>edges;
    int d[maxn];
    int vis[maxn];
    int ok[maxn];

    void init(){
        for(int i=0;i<maxn;i++){
            G[i].clear();
        }
        edges.clear();
    }

    void add_edges(int u,int v,int w){
        edges.push_back(Edge(u,v,w));
        int x=edges.size();
        G[u].push_back(x-1);
    }

    bool dijkstra(int s,int t){
    memset(vis,0,sizeof(vis));
    memset(ok,0,sizeof(ok));
    for(int i=0;i<maxn;i++)d[i]=INF;
    d[s]=0;
    vis[s]=1;
    priority_queue<HeapNode>Q;
    Q.push(HeapNode(0,s));
    while(Q.size()){
        HeapNode x=Q.top();Q.pop();
        int u=x.u;
        for(int i=0;i<G[u].size();i++){
            Edge &e=edges[G[u][i]];
            if(!vis[e.to]&&d[u]+e.w<d[e.to]){
                ok[G[u][i]]++;
                d[e.to]=d[u]+e.w;
                Q.push(HeapNode(d[e.to],e.to));
                vis[e.to]=1;
            }
        }
    }
    return vis[t];
}
};
Dijkstra dij;

struct Edge1
{
    int from,to,cap,flow;
    Edge1(int u,int v,int cap,int flow):from(u),to(v),cap(cap),flow(flow){}
};
struct Dinic
{
    int n,m,s,t;
    vector<int>G[maxn];
    vector<Edge1>edges;
    bool vis[maxn];
    int d[maxn];
    int cur[maxn];
    void add_edges(int u,int v,int cap)
    {
        edges.push_back(Edge1(u,v,cap,0));
        edges.push_back(Edge1(v,u,0,0));
        m=edges.size();
        G[u].push_back(m-2);
        G[v].push_back(m-1);
    }
    void init(int n)
    {
        this->n=n;
        for(int i=0;i<maxn;i++)G[i].clear();
        edges.clear();
    }
    //BFS构造层次图
    bool BFS()
    {
        memset(vis,0,sizeof(vis));
        memset(d,0,sizeof(d));
        queue<int>Q;
        Q.push(s);
        d[s]=0;
        vis[s]=1;
        while(!Q.empty())
        {
            int x=Q.front();Q.pop();
            for(int i=0;i<G[x].size();i++)
            {
                Edge1 &e=edges[G[x][i]];
                if(!vis[e.to]&&e.cap>e.flow)
                {
                    vis[e.to]=1;
                    d[e.to]=d[x]+1;
                    Q.push(e.to);
                }
            }
        }
        return vis[t];
    }
    //沿着层次图增广
    int DFS(int x,int a)
    {
        if(x==t||a==0)return a;
        int flow=0,f;
        for(int &i=cur[x];i<G[x].size();i++)
        {
            Edge1 &e=edges[G[x][i]];
            if(d[x]+1==d[e.to]&&(f=DFS(e.to,min(a,e.cap-e.flow)))>0)
            {
                e.flow+=f;
                edges[G[x][i]^1].flow-=f;
                flow+=f;
                a-=f;
                if(a==0)break;
            }
        }
        return flow;
    }

    int Maxflow(int s,int t)
    {
        this->s=s;
        this->t=t;
        int flow=0;
        while(BFS())
        {
            memset(cur,0,sizeof(cur));
            flow+=DFS(s,INF);
        }
        return flow;
    }
};
Dinic dinic;
int n,m;
signed main(){
    int T;
    scanf("%lld",&T);
    while(T--){
        dij.init();
        dinic.init(2*n);
        scanf("%lld%lld",&n,&m);
        for(int i=1;i<=m;i++){
            int u,v,w;
            scanf("%lld%lld%lld",&u,&v,&w);
            dij.add_edges(u,v,w);
        }
        if(!dij.dijkstra(1,n)){
            puts("0");
            break;
        }
        for(int i=1;i<=n;i++){
            for(int j=0;j<dij.G[i].size();j++){
                Edge &e=dij.edges[dij.G[i][j]];
                if(dij.d[e.to]==dij.d[i]+e.w){
                    dinic.add_edges(i,e.to,e.w);
                }
            }
        }
        printf("%lld\n",dinic.Maxflow(1,n));
    }
    return 0;
}

/*

3
5 5
1 2 5
2 3 3
3 5 3
4 5 3
2 4 3
3 4
1 2 1
2 3 1
1 3 2
1 3 3

*/
  • 1002.Operation

http://acm.hdu.edu.cn/showproblem.php?pid=6579

题目描述:给你初始一个数组,有两种操作:

1.询问[l,r]中区间异或和最大是多少

2.将数组最后加一个数

本题强制在线。

数据范围很大,线段树维护区间线性基必然超时,必须考虑新的方法。

这里粘贴大神博客,有助于加强线性基的理解

https://blog.csdn.net/henu_jizhideqingwa/article/details/96893284

  • 1001.Blank

http://acm.hdu.edu.cn/showproblem.php?pid=6578

题目描述:有很多个空格,你要在空格内填数,有0,1,2,3四种数,还有许多约束条件,对于每个区间[l,r],规定只能有x种不同的数。问你最后有多少种满足条件的方案,答案对998244353取模。

答案思路:

定义 dp[i][j][k][t] 代表填完前 t 个位置后,{0, 1, 2, 3} 4 个数字最后一次出现的位置,

排序后为 i, j, k, t(i < j < k < t) 的方案数目,则按照第 t + 1 位的数字的四种选择,可以得

到四种转移。

对于限制可以按照限制区间的右端点分类,求出 dp[i][j][k][t] 后,找到所有以 t 为区间

右端点的限制条件,如果当前状态不满足所有限制条件则不合法,不再向后转移。

总时间复杂度 O(n^4)

滚动一维,空间复杂度 O(n^3)

理解:

先附上标程代码:

#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long ll;

typedef pair<int, int> piir;

const int N = 100 + 5;

int n, m, ans;

int dp[2][N][N][N];

vector <piir> lims[N];

inline void Mod(int &x) {
    static int mod = 998244353;//1e9 + 7;
    if (x >= mod) x -= mod;
}

int main() {
    int Case, ans, l, r, x;
    int i, j, k, t, p;
    for (scanf("%d", &Case); Case --; ) {
        ans = 0;
        scanf("%d %d", &n, &m);
        for (i = 1; i <= n; i ++)
            lims[i].clear();
        for (i = 0; i < m; i ++) {
            scanf("%d %d %d", &l, &r, &x);
            lims[r].push_back(piir(l, x));
        }
        dp[0][0][0][0] = 1;
        //n的数据范围是1-n,按顺序遍历结尾
        for (i = p = 1; i <= n; i ++, p ^= 1) {
            //滚动数组的目的就是记录前一次的状态
            //数组初始化,滚动数组把当前状态置为0
            for (j = 0; j <= i; j ++)
                for (k = 0; k <= j; k ++)
                    for (t = 0; t <= k; t ++)
                        dp[p][j][k][t] = 0;


            for (j = 0; j < i; j ++)
                for (k = 0; k <= j; k ++)
                    for (t = 0; t <= k; t ++) {
                        //状态转移方程
                        //当前位置选的数与上一个相同
                        Mod(dp[p][j][k][t] += dp[p ^ 1][j][k][t]);
                        //当前位置的上一位为j,记录状态i-1
                        Mod(dp[p][i - 1][k][t] += dp[p ^ 1][j][k][t]);
                        //当前位置的上一位为k,,记录状态i-1
                        Mod(dp[p][i - 1][j][t] += dp[p ^ 1][j][k][t]);
                        //当前位置的上一位为t,记录状态i-1
                        Mod(dp[p][i - 1][j][k] += dp[p ^ 1][j][k][t]);
                    }

            for (j = 0; j < i; j ++)
                for (k = 0; k <= j; k ++)
                    for (t = 0; t <= k; t ++)
                        for (piir tmp : lims[i])
                            //放在r的位置上肯定合法,只需判断从l到r-1是否合法
                            if (1 + (j >= tmp.first) + (k >= tmp.first) + (t >= tmp.first) != tmp.second)
                                dp[p][j][k][t] = 0;
        }
        for (i = 0, p = n & 1; i < n; i ++)
            for (j = 0; j <= i; j ++)
                for (k = 0; k <= j; k ++)
                    Mod(ans += dp[p][i][j][k]);
        printf("%d\n", ans);
    }
    return 0;
}

 

dp数组滚动时,通常需要记录上一次状态和当前状态,用p代表上一状态,p^1代表前一状态,这样可以少做一维状态的记录。

滚动数组解决之后,我们就得弄懂答案中说的四种转移方程是怎样的

看代码:

Mod(dp[p][j][k][t] += dp[p ^ 1][j][k][t]);
Mod(dp[p][i - 1][k][t] += dp[p ^ 1][j][k][t]);
Mod(dp[p][i - 1][j][t] += dp[p ^ 1][j][k][t]);
Mod(dp[p][i - 1][j][k] += dp[p ^ 1][j][k][t]);

1.当前选择的数与上一个相同,那么上一个状态会被当前状态覆盖,不记录,直接转移

2.当前选择的数的上一个为j,那么j此时不需要记录,而上一个状态则需要记录,所以要加上dp[p^1][j][k][t];

3.当前选择的数的上一个为k,与2同理

4.当前选择的数的上一个为t,与2同理

最后在判断条件是否满足时,因为是首先将右端点r安排数字,所以右端点一定满足条件,直接加上1,再判断从左端点开始的每个数是否满足条件即可。

​​​​​​​

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值