zoukankan      html  css  js  c++  java
  • qbxt 10 月模拟赛 Day4

    A

    二分答案 (x),加入所有 (leq x) 的边后判断图是否强连通即可。

    一种方法是判联通性+tarjan,但是可以建正向图和反向图分别从 (1) 开始 dfs 。(这样说明 (1) 和任何点都有两条路径,所以任意两点都有两条路径)

    有向图 tarjan 不要判 father,,一开始因为这个吃了罚时。

    #include <bits/stdc++.h>
    
    #define fi first
    #define se second
    #define db double
    #define U unsigned
    #define P std::pair<int,int>
    #define LL long long
    #define pb push_back
    #define MP std::make_pair
    #define all(x) x.begin(),x.end()
    #define CLR(i,a) memset(i,a,sizeof(i))
    #define FOR(i,a,b) for(int i = a;i <= b;++i)
    #define ROF(i,a,b) for(int i = a;i >= b;--i)
    #define DEBUG(x) std::cerr << #x << '=' << x << std::endl
    
    const int MAXN = 2e5 + 5;
    
    std::vector<int> G[MAXN];
    
    struct Edge{
        int u,v,w;
    
        inline bool operator < (const Edge &t) const {
            return w < t.w;
        }
    }e[MAXN];
    
    int n,m;
    int dfn[MAXN],low[MAXN],ts;
    bool ins[MAXN];
    int stk[MAXN],tp;
    int t = 0;
    
    inline void dfs(int v){
        dfn[v] = low[v] = ++ts;ins[v] = 1;stk[++tp] = v;
        for(auto x:G[v]){
            if(!dfn[x]){
                dfs(x);
                low[v] = std::min(low[v],low[x]);
            }
            else if(ins[x]) low[v] = std::min(low[v],dfn[x]);
        }
        if(low[v] == dfn[v]){
            ++t;int y = -1;
            do{
                y = stk[tp--];
                ins[y] = 0;
            }while(y != v);
        }
    }
    
    inline bool chk(int k){
        FOR(i,1,n) G[i].clear(),ins[i] = dfn[i] = low[i] = 0;
        FOR(i,1,k) G[e[i].u].pb(e[i].v);
        ts = t = 0;dfs(1);bool flag = 1;
        FOR(i,1,n) flag &= (dfn[i] != 0);
        return t == 1 && flag;
    }
    
    int main(){
    //    freopen("A.in","r",stdin);
        scanf("%d%d",&n,&m);
        FOR(i,1,m) scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
        std::sort(e+1,e+m+1);
        int l = 1,r = m,ans = -1;
        while(l <= r){
            int mid = (l + r) >> 1;
            if(chk(mid)) ans = mid,r = mid-1;
            else l = mid+1;
        }
        printf("%d
    ",ans==-1?ans:e[ans].w);
        return 0;
    }
    

    B

    设询问是 (u o lca o v),把答案分成三部分:(u o lca) 的点之间的贡献, (v o lca) 之间的贡献,(u o lca)(lca o v) 的贡献。

    (u o lca)(lca o v) 之间的贡献求个链上和就行了。

    (u o lca) 的贡献可以先处理处到根的贡献,然后减去多余的(多余的分两类,(lca) 到根内部的贡献和 (lca) 到根与 (lca)(u) 之间的贡献)

    #include <bits/stdc++.h>
    
    #define fi first
    #define se second
    #define db double
    #define U unsigned
    #define P std::pair<int,int>
    #define LL long long
    #define pb push_back
    #define MP std::make_pair
    #define all(x) x.begin(),x.end()
    #define CLR(i,a) memset(i,a,sizeof(i))
    #define FOR(i,a,b) for(int i = a;i <= b;++i)
    #define ROF(i,a,b) for(int i = a;i >= b;--i)
    #define DEBUG(x) std::cerr << #x << '=' << x << std::endl
    
    const int MAXN = 1e5 + 5;
    const int MAXM = 16;
    
    struct Edge{
        int to,nxt;
    }e[MAXN<<1];
    int head[MAXN],cnt;
    
    inline void add(int u,int v){
        e[++cnt] = (Edge){v,head[u]};head[u] = cnt;
    }
    
    int n,m;
    int f[MAXN][MAXM+1],dep[MAXN];
    int a[MAXN],b[MAXN];
    LL sma[MAXN],smb[MAXN];
    LL ra[MAXN],rb[MAXN];
    
    inline void dfs(int v,int fa=0){
        f[v][0] = fa;dep[v] = dep[fa]+1;
        FOR(i,1,MAXM) f[v][i] = f[f[v][i-1]][i-1];
        sma[v] = sma[fa]+a[v];
        smb[v] = smb[fa]+b[v];
        ra[v] = ra[fa]+a[v]*smb[fa];
        rb[v] = rb[fa]+b[v]*sma[fa];
        for(int i = head[v];i;i = e[i].nxt){
            if(e[i].to == fa) continue;
            dfs(e[i].to,v);
        }
    }
    
    inline int lca(int x,int y){
        if(dep[x] != dep[y]){
            if(dep[x] < dep[y]) std::swap(x,y);
            int d = dep[x]-dep[y];
            FOR(i,0,MAXM){
                if((d>>i)&1) x = f[x][i];
            }
        }
        if(x == y) return x;
        ROF(i,MAXM,0){
            if(f[x][i] == f[y][i]) continue;
            x = f[x][i],y = f[y][i];
        }
        return f[x][0];
    }
    
    int main(){
        scanf("%d%d",&n,&m);
        FOR(i,2,n){
            int f;scanf("%d",&f);add(f,i);
        }
        FOR(i,1,n) scanf("%d",a+i);
        FOR(i,1,n) scanf("%d",b+i);
        dfs(1);
        FOR(i,1,m){
            int x,y;scanf("%d%d",&x,&y);
            int l = lca(x,y);
            LL ans = (sma[x]-sma[l])*(smb[y]-smb[l]);
            ans += ra[x]+rb[y]-ra[f[l][0]]-rb[f[l][0]];
            ans -= (sma[x]-sma[f[l][0]])*smb[f[l][0]];
    //        DEBUG(ans);
            ans -= (smb[y]-smb[f[l][0]])*sma[f[l][0]];
            printf("%lld
    ",ans);
        }
        return 0;
    }
    

    C

    (f_{i,j}) 表示前 (i) 个数,第 (i) 个数填了颜色 (j) 的答案,为了方便转移设 (g_{i,j}) 表示考虑前 (i) 个数,第 (i) 个数不填颜色 (j) 的答案,转移就有 (f_{i,j} = g_{i-1,j})(g_i) 可以处理前缀后缀 min 方便求出。

    求答案我们考虑去枚举相同颜色的区间和颜色,那么我们要对前缀后缀都求出一个 (g),设为 (g_1,g_2)

    (sm_{i,j}) 表示颜色 (i) 的前 (j) 的和,枚举区间 (l,r) 涂成一种颜色 (c),答案就是 (sm_{c,r}-sm_{c,l-1}+g_{l-1,c}+g_{r+1,c}),直接枚举是 (O(n^3)) 的,但是我们可以先枚举颜色和左端点,左端点从右到左枚举,变成了一个滑动窗口问题,单调队列维护即可。

    #include <bits/stdc++.h>
    
    #define fi first
    #define se second
    #define db double
    #define U unsigned
    #define P std::pair<int,int>
    #define LL long long
    #define pb push_back
    #define MP std::make_pair
    #define all(x) x.begin(),x.end()
    #define CLR(i,a) memset(i,a,sizeof(i))
    #define FOR(i,a,b) for(int i = a;i <= b;++i)
    #define ROF(i,a,b) for(int i = a;i >= b;--i)
    #define DEBUG(x) std::cerr << #x << '=' << x << std::endl
    
    inline char nc(){
        #define SIZE 1000000+3
        static char buf[SIZE],*p1 = buf+SIZE,*p2 = buf+SIZE;
        if(p1 == p2){
            p1 = buf;p2 = buf+fread(buf,1,SIZE,stdin);
            if(p1 == p2) return -1;
        }
        return *p1++;
        #undef SIZE
    }
    
    template <typename T>
    inline void read(T &x){
        x = 0;int flag = 0;char ch = nc();
        while(!isdigit(ch)){
            if(ch == '-') flag = 1;
            ch = nc();
        }
        while(isdigit(ch)){
            x = (x<<1) + (x<<3) + (ch^'0');
            ch = nc();
        }
        if(flag) x = -x;
    }
    
    const int MAXN = 2000+5;
    int g1[MAXN][MAXN],g2[MAXN][MAXN],f[MAXN][MAXN];
    int n,m,k;
    int a[MAXN][MAXN];
    int pre[MAXN],suf[MAXN];
    int sm[MAXN][MAXN];
    
    inline void init(int f[],int g[]){
        pre[0] = suf[m+1] = 1e9;
        FOR(i,1,m) pre[i] = std::min(pre[i-1],f[i]);
        ROF(i,m,1) suf[i] = std::min(suf[i+1],f[i]);
        FOR(i,1,m) g[i] = std::min(pre[i-1],suf[i+1]);
    }
    
    int h[MAXN];
    
    int main(){
        read(n);read(m);read(k);
        FOR(i,1,n) FOR(j,1,m) read(a[i][j]);
        FOR(i,1,m) FOR(j,1,n) sm[i][j] = sm[i][j-1]+a[j][i];
        FOR(i,1,m) f[1][i] = a[1][i];
        init(f[1],g1[1]);
        FOR(i,2,n){
            FOR(j,1,m){
                f[i][j] = g1[i-1][j]+a[i][j];
            }
            init(f[i],g1[i]);
        }
        FOR(i,1,m) f[n][i] = a[n][i];
        init(f[n],g2[n]);
        ROF(i,n-1,1){
            FOR(j,1,m){
                f[i][j] = g2[i+1][j]+a[i][j];
            }
            init(f[i],g2[i]);
        }
        // 选择区间[l,r]涂成 颜色 v
        // sm[v][r]+g2[r+1][v] +g1[l-1][v]-sm[v][l-1]
        // h1[i] = min_v g1[i][v]-sm[v][i]
        // h2[i] = min_v sm[v][i]+g2[i+1][v]
        // h1[i-1]+h2[i]
        int ans = 1e9;
        FOR(v,1,m){
            //h2[n+1] = 1e9;
            std::deque<int> q;
            FOR(i,1,n) h[i] = g2[i+1][v]+sm[v][i];
            ROF(l,n,1){
                // sm[v][r]+g2[r+1][v]+g1[l-1][v]-sm[v][l-1]
                while(!q.empty() && q[0]-l+1 > k) q.pop_front();
                while(!q.empty() && h[q.back()] >= h[l]) q.pop_back();
                q.pb(l);
                ans = std::min(ans,h[q.front()]+g1[l-1][v]-sm[v][l-1]);
            }
        }
        printf("%d
    ",ans);
        return 0;
    }
    

    D

    一开始傻了。。这个题不应该直接在 AC 自动机上跑,可以搞下来跑。

    建出 AC 自动机,相当于边有边权,要求经过某些点的最短路径。可以拆点+bfs 求出每个好点到所有点的最短路(钦定不能走坏点),状压 dp 即可:设 (f_{v,S}) 表示走完了 (S) 内的点,当前在 (v) 的答案,转移枚举下一个怎么走即可。

    可能是状态不满。。?反正过了。考试的时候也要写啊。。

    #pragma GCC optimize("Ofast")
    #include <bits/stdc++.h>
    
    #define fi first
    #define se second
    #define db double
    #define U unsigned
    #define P std::pair<int,int>
    #define LL long long
    #define pb push_back
    #define MP std::make_pair
    #define all(x) x.begin(),x.end()
    #define CLR(i,a) memset(i,a,sizeof(i))
    #define FOR(i,a,b) for(int i = a;i <= b;++i)
    #define ROF(i,a,b) for(int i = a;i >= b;--i)
    #define DEBUG(x) std::cerr << #x << '=' << x << std::endl
    
    const int MAXN = 1e5 + 5;
    int ch[MAXN][4],tot=1,fail[MAXN],rt = 1;
    int n,m,k;
    bool bd[MAXN];
    int G[MAXN];
    
    inline void insert(char str[],int opt,int id=0){
        int v = rt;
        FOR(i,1,k){
            int o = str[i]-'1';
            if(!ch[v][o]) ch[v][o] = ++tot;
            v = ch[v][o];
        }
        if(opt == 1) G[id] = v;
        else bd[v] = 1;
    }
    
    struct Edge{
        int to,w,nxt;
    }e[MAXN<<2];
    int head[MAXN],cnt;
    
    inline void add(int u,int v,int w){
        if(bd[u] || bd[v]) return;
    //    printf("%d %d %d
    ",u,v,w);
        e[++cnt] = (Edge){v,w,head[u]};head[u] = cnt;
    }
    
    inline void build(){
        std::queue<int> q;
        FOR(i,0,3){
            if(ch[rt][i]) fail[ch[rt][i]] = rt,q.push(ch[rt][i]);
            else ch[rt][i] = rt;
        }
        while(!q.empty()){
            int v = q.front();q.pop();
            FOR(i,0,3){
                if(ch[v][i]) fail[ch[v][i]] = ch[fail[v]][i],q.push(ch[v][i]);
                else ch[v][i] = ch[fail[v]][i];
            }
        }
        FOR(i,1,tot) FOR(j,0,3) add(i,ch[i][j],j+1);
    }
    
    char str[123];
    int dis[21][MAXN];
    bool used[MAXN];
    
    inline void dij(int dis[],int s){
        std::priority_queue<P,std::vector<P>,std::greater<P> > q;
        FOR(i,1,tot) dis[i] = 1e9,used[i] = 0;
        q.push(MP(dis[s]=0,s));
        while(!q.empty()){
            int v = q.top().se;q.pop();
            if(used[v]) continue;
            used[v] = 1;
            for(int i = head[v];i;i = e[i].nxt){
                if(bd[e[i].to]) continue;
                if(dis[e[i].to] > dis[v] + e[i].w){
                    dis[e[i].to] = dis[v]+e[i].w;
                    q.push(MP(dis[e[i].to],e[i].to));
                }
            }
        }
    }
    
    int f[(1<<20)+5][20];
    
    int main(){
    //    int sz = sizeof(f)+sizeof(ch)+sizeof(fail)+sizeof(bd)+sizeof(G)+sizeof(e)+sizeof(head)+sizeof(used)+sizeof(dis);
    //    DEBUG(sz/1024/1024);
    //    freopen("D.in","r",stdin);
    //    freopen("D.out","w",stdout);
        scanf("%d%d%d",&n,&m,&k);
        FOR(i,1,n) scanf("%s",str+1),insert(str,1,i);
        FOR(i,1,m) scanf("%s",str+1),insert(str,2);
        build();
        dij(dis[0],1);
        FOR(i,1,n) dij(dis[i],G[i]);
        CLR(f,0x3f);
        FOR(i,0,n-1) f[(1<<i)][i] = dis[0][G[i+1]];
        FOR(S,1,(1<<n)-1){
            FOR(i,0,n-1){
                if(f[S][i] == 0x3f3f3f3f) continue;
                if(!((S>>i)&1)) continue;
                FOR(j,0,n-1){
                    if((S>>j)&1) continue;
                    f[S^(1<<j)][j] = std::min(f[S^(1<<j)][j],f[S][i]+dis[i+1][G[j+1]]);
                }
            }
        }
        int ans = 1e9;
        FOR(i,0,n-1) ans = std::min(ans,f[(1<<n)-1][i]);
        printf("%d
    ",ans);
        return 0;
    }
    
  • 相关阅读:
    Web前端一种动态样式语言-- Less
    Windows下查看8080进程及结束进程命令
    Java应用程序实现屏幕的"拍照"
    批处理命令 BAT备份MySQL数据库
    LineNumberReader类
    SAXReader
    linux打包压缩命令汇总
    Jquery获取选中的checkbox的值
    Jquery_联系电话正则表达式
    CSS overflow 属性
  • 原文地址:https://www.cnblogs.com/rainair/p/14305840.html
Copyright © 2011-2022 走看看