牛客CSP-S提升組賽前集訓營5 解題報告

Linkernode

總分 : 100 + 100 + 40 = 240c++

T1

結論題。不管如何神J都會贏。spa

最優決策:神樹變化了我就不變,神樹不變我就變化。debug

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;


const int mod=998244353;

inline ll POW(ll pre, int x){
    ll res=1;
    for(; x; x>>=1,pre=pre*pre%mod)
        if(x&1) res=res*pre%mod;
    return res;
}

int n;


int main(){
    scanf("%d",&n);
    printf("%lld\n",POW(2,n));
    return 0;
}

T2

狀壓DP水題。code

暴力都沒有去切,由於k過小了,直接狀壓走過那些邊便可。ip

//代碼有點醜,可是考試的時候挺快就敲出來了。
#include<bits/stdc++.h>
#define re register
#define rep(i,a,b) for(re int i=a,i##end=b; i<=i##end; i++)
#define drep(i,a,b) for(re int i=a,i##end=b; i>=i##end; i--)
#define repp(i,a,b) for(re int i=a,i##end=b; i<i##end; i++)
#define drepp(i,a,b) for(re int i=a,i##end=b; i>i##end; i--)
#define Erep(i,x) for(re int i=head[x]; i; i=Edge[i].nxt)
#define lowbit(x) ((x)&-(x))
#define debug(x) cerr<<#x<<" = "<<x<<endl
#define ms(x,a) memset(x,a,sizeof x)
#define PII pair<int,int>
#define PLL pair<ll,ll>
#define fi first
#define se second
#define coint const int
#define coll const ll
#define CM cerr<<(&S2-&S1)/1024./1024<<"MB"<<endl
typedef long long ll;
using namespace std;
template<class T>inline T rd(){
    static char ch;static bool neg;static T x;
    for(ch=0, neg=0; ch>'9'||ch<'0'; neg|=(ch=='-'),ch=getchar());
    for(x=0; ch>='0'&&ch<='9'; x=(x<<1)+(x<<3)+(ch^'0'),ch=getchar());
    return neg?-x:x;
}
template<class T>inline T Max(const T &x, const T &y) { return x>y?x:y; }
template<class T>inline T Min(const T &x, const T &y) { return x<y?x:y; }

bool S1;

coint N=50000+5,M=200000+5,K=12+5;
struct edge{
    int to,val,nxt;
}Edge[M<<1];
int head[N],tcnt;
inline void AddEdge(coint u, coint v, coint w){
    Edge[++tcnt]=(edge)<%v,w,head[u]%>;
    head[u]=tcnt; return;
}

struct edge2{
    int to,val,id;
};
vector<edge2>G[N];
vector<int>vec;

int n,m,k;
int mark[N],ID[N];
ll dis[K<<1][N];
ll dp[(1<<K)|5][K<<1];

struct node{
    int x;
    ll val;
    bool operator < (const node &_) const { return val>_.val; }
};

struct Heap{
    node sum[N+M];
    int sz;
    inline void pop(){
        sum[1]=sum[sz--];
        int now=1,nxt;
        while(nxt=now<<1,nxt<=sz){
            if(nxt<sz && sum[nxt]<sum[nxt|1]) nxt|=1;
            if(sum[now]<sum[nxt]) swap(sum[nxt],sum[now]),now=nxt;
            else break;
        }
        return;
    }
    inline void push(node x){
        sum[++sz]=x;
        int now=sz,nxt;
        while(nxt=now>>1,nxt){
            if(sum[nxt]<sum[now]) swap(sum[now],sum[nxt]),now=nxt;
            else break;
        }
        return;
    }
    inline bool empty() { return !sz; }
    inline void clear() { sz=0; return; }
    inline int size() { return sz; }
    inline node top() { return sum[1]; }
}Q;

//priority_queue<node>Q;

inline void Dijkstra(coint id, coint st){
    Q.clear();
    Q.push((node)<%st,0%>);
    dis[id][st]=0;
    while(!Q.empty()){
        node now=Q.top(); Q.pop();
        if(mark[now.x]==id) continue;
        mark[now.x]=id;
        Erep(i,now.x){
            edge y=Edge[i];
            coll res=y.val+now.val;
            if(res<dis[id][y.to]){
                dis[id][y.to]=res;
                Q.push((node)<%y.to,res%>);
            }
        }
    }
//  rep(i,1,n) printf("%d -> %d : %lld\n",st,i,dis[id][i]);
    return;
}

bool S2;

int main(){
//  CM;
//  freopen("test.in","r",stdin);
//  freopen("test.out","w",stdout);
    n=rd<int>(),m=rd<int>(),k=rd<int>();
    rep(i,1,m){
        coint u=rd<int>(),v=rd<int>(),w=rd<int>();
        AddEdge(u,v,w); AddEdge(v,u,w);
        if(i<=k){
            G[u].push_back((edge2)<%v,w,i%>);
            if(u!=v) G[v].push_back((edge2)<%u,w,i%>);
            mark[u]=mark[v]=1;
        }
    }
    mark[1]=1;
    rep(i,1,n) if(mark[i]) vec.push_back((int)i),ID[i]=((int)vec.size())-1;
    ms(mark,-1); ms(dis,0x3f);
    repp(i,0,vec.size()) Dijkstra(i,vec[i]);
    ms(dp,0x3f);
    coll INF=dp[0][0];
    dp[0][0]=0;
    repp(sta,0,(1<<k)-1){
        repp(i,0,vec.size()){
            repp(j,0,vec.size()){
                dp[sta][j]=Min(dp[sta][j],dp[sta][i]+dis[i][vec[j]]);
            }
        }
        repp(i,0,vec.size()){
            if(dp[sta][i]>=INF) continue;
            int at=vec[i];
            repp(j,0,(int)G[at].size()){
                coint to=G[at][j].to,val=G[at][j].val,id=G[at][j].id;
                if(sta&(1<<id>>1)) continue;
                dp[sta|(1<<id>>1)][ID[to]]=Min(dp[sta|(1<<id>>1)][ID[to]],dp[sta][i]+val);
            }
        }
    }
    ll ans=INF;
    repp(i,0,vec.size())
        ans=Min(ans,dp[(1<<k)-1][i]+dis[i][1]);
    printf("%lld\n",ans);
    return 0;
}

T3

(pre_dfs與dfs_top是樹鏈剖分的部分。)get

P30

直接暴力\(O(n^3)\)處理處每兩個點間最小Dis值it

struct P30{
    static coint N=300+5;
    ll Dis[N][N];
    inline void solve(){
        pre_dfs(1,0); dfs_top(1,0,1);
        rep(i,1,n){
            int x=i;
            while(x) Dis[x][i]=x*(dis[i]-dis[x]),x=f[x];
        }
        rep(i,1,n){
            int x=i;
            while(x){
                int y=x;
                while(y){
                    Dis[y][i]=Min(Dis[y][i],Dis[y][x]+Dis[x][i]);
                    y=f[y];
                }
                x=f[x];
            }
        }
        rep(i,1,m){
            int s=rd<int>(),t=rd<int>();
            if(LCA(s,t)!=s) { puts("-1"); continue; }
            printf("%lld\n",Dis[s][t]);
        }
        return;
    }
}p30;

P40

能夠發現詢問數量很小,咱們直接對於每一個詢問暴力向上跳,而咱們總共就會處理\(n^2\)個點之間的距離,中間重複處理的還能夠省去,所以總複雜度不到\(O(n^2)\)io

struct P40{
    static coint N=3000+5;
    ll Dis[N][N];
    bool mark[N][N];
    inline void solve(){
        pre_dfs(1,0); dfs_top(1,0,1);
        rep(i,1,n){
            Dis[i][i]=0;
            int x=f[i];
            while(x) Dis[x][i]=x*(dis[i]-dis[x]),x=f[x];
        }
        rep(i,1,m){
            int s=rd<int>(),t=rd<int>();
            if(LCA(s,t)!=s) { puts("-1"); continue; }
            int x=t,fa=f[s];
            if(!mark[s][t]) while(x!=fa){
                if(mark[x][t]) { x=f[x]; continue; }
                int y=t;
                while(y!=x){
                    Dis[x][t]=Min(Dis[x][t],Dis[x][y]+Dis[y][t]);
                    y=f[y];
                }
                mark[x][t]=1;
                x=f[x];
            }
            printf("%lld\n",Dis[s][t]);
        }
        return;
    }
}p40;

P70

能夠發如今向下跳的過程當中,咱們通過的點是單調遞減的,所以利用倍增思想,記錄下一個走到的點,以及其花費,而後倍增處理處便可。class

struct P_Chain{
    ll dis[N];
    ll Dis[20][N];
    int tp[20][N];
    int dep[N],f[N];
    void dfs(coint x, coint fa){
        Erep(i,x){
            edge y=Edge[i];
            if(y.to==fa) continue;
            dep[y.to]=dep[x]+1; f[y.to]=x; dis[y.to]=dis[x]+y.val;
            dfs(y.to,x);
            if(y.to<x) tp[0][x]=y.to;
            else{
                int z=tp[0][y.to];
                while(z>=x && z) z=tp[0][z];
                if(z) tp[0][x]=z;
            }
            if(tp[0][x]) Dis[0][x]=x*(dis[tp[0][x]]-dis[x]);
        }
        return;
    }
    inline void solve(){
        dfs(1,0);
        rep(j,1,19){
            rep(i,1,n){
                if(!tp[j-1][i] || !tp[j-1][tp[j-1][i]]) continue;
                tp[j][i]=tp[j-1][tp[j-1][i]];
                Dis[j][i]=Dis[j-1][i]+Dis[j-1][tp[j-1][i]];
            }
        }
        rep(i,1,m){
            int x=rd<int>(),y=rd<int>();
            if(dep[x]>dep[y]) { puts("-1"); continue; }
            ll ans=0;
            drep(j,19,0){
                if(dep[tp[j][x]]<dep[y] && tp[j][x]){
                    ans+=Dis[j][x];
                    x=tp[j][x];
                }
            }
            ans+=1ll*(dis[y]-dis[x])*x;
            printf("%lld\n",ans);
        }
        return;
    }
}p_chain;

P100

其實跟鏈的差很少。

咱們把整棵樹分紅多個鏈,再處理鏈跳到鏈的狀況便可。

//這裏從新寫了pre_dfs與dfs_top,由於有不一樣的東西要用到

struct P100{
    
    int dep[N],f[N],sz[N],son[N],top[N],L[N],R[N],dfn;
    ll dis[N],Dis[22][N];
    int to[22][N];
    int A[N],stk[N];
    
    void pre_dfs(coint x, coint fa){
        sz[x]=1;
        Erep(i,x){
            edge y=Edge[i];
            if(y.to==fa) continue;
            dep[y.to]=dep[x]+1; f[y.to]=x; dis[y.to]=dis[x]+y.val;
            pre_dfs(y.to,x);
            sz[x]+=sz[y.to];
            if(sz[son[x]]<sz[y.to]) son[x]=y.to;
        }
        return;
    }
    
    void dfs_top(coint x, coint fa, coint tp){
        top[x]=tp; L[x]=++dfn;
        coint SON=son[x];
        if(SON) dfs_top(SON,x,tp);
        Erep(i,x){
            int y=Edge[i].to;
            if(y==fa || y==SON) continue;
            dfs_top(y,x,y);
        } R[x]=dfn;
        if(top[x]==x){
            int cnt=0,tail=0;
            for(int i=x; i; i=son[i]) A[++cnt]=i;
            drep(i,cnt,1){
                int x=A[i];
                while(tail && stk[tail]>=x) tail--;
                if(tail){
                    Dis[0][x]=x*(dis[stk[tail]]-dis[x]);
                    to[0][x]=stk[tail];
                }
                stk[++tail]=x;
            }
        }
        return;
    }
    
    struct Element{
        int l,r;
    }B[N];
    
    inline int Get_Element(int x, int y){
        int tpx=top[x],tpy=top[y],cnt=0;
        while(tpx!=tpy){
            B[++cnt]=(Element)<%tpy,y%>;
            y=f[tpy]; tpy=top[y];
        }
        B[++cnt]=(Element)<%x,y%>;
        return cnt;
    }
    
    inline void solve(){
        pre_dfs(1,0); dfs_top(1,0,1);
        rep(j,1,19){
            rep(i,1,n){
                to[j][i]=to[j-1][to[j-1][i]];
                Dis[j][i]=Dis[j-1][i]+Dis[j-1][to[j-1][i]];
            }
        }
        while(m--){
            int s=rd<int>(),t=rd<int>();
            if(s==t) { puts("0"); continue; }
            if(L[t]<L[s] || L[t]>R[s]) { puts("-1"); continue; }
            int cnt=Get_Element(s,t);
            ll ans=0;
            int mn=s;
            drep(i,cnt,1){
                int l=B[i].l,r=B[i].r;
                mn=Min(mn,l);
                drep(j,19,0){
                    int nxt_l=to[j][l];
                    if(nxt_l && L[nxt_l]<=L[r] && nxt_l>=mn) ans+=mn*(dis[nxt_l]-dis[l]),l=nxt_l;
                }
                if(to[0][l] && L[to[0][l]]<=L[r]) ans+=mn*(dis[to[0][l]]-dis[l]),l=to[0][l],mn=l;
                drep(j,19,0){
                    int nxt_l=to[j][l];
                    if(nxt_l && L[nxt_l]<=L[r]) ans+=Dis[j][l],mn=l=to[j][l];
                }
                if(i>1) ans+=mn*(dis[B[i-1].l]-dis[l]);
                else ans+=mn*(dis[r]-dis[l]);
            }
            printf("%lld\n",ans);
        }
        return;
    } 
}p100;

總結

鏈的30分沒拿到真的不該該。思惟還得更加活躍才能夠。

相關文章
相關標籤/搜索