zoukankan      html  css  js  c++  java
  • 【题解】2020 年电子科技大学 ACM-ICPC 暑假前集训

    比赛地址

    A - 软件工程

    对于一条边(i o j)

    最早的开始时间有递推关系(f[j]=max{f[i]+w[i]}),正向跑一边DAG即可

    最晚的开始时间有递推关系(g[i]=min{g[j]-w[i]}),反向跑一边DAG即可

    #include <cstdio>
    #include <iostream>
    #include <climits>
    using namespace std;
    typedef long long ll;
    const ll N=5*1e5+2,M=1e6+1;
    ll n,m,a,b,c,ans,t[N],s[N],d[N],sz,sz2,vis[N],vis2[N],head[N],head2[N];
    struct E{
        ll next,to;
    }e[M*2],e2[M*2];
    void insert(ll a,ll b){
        sz++;
        e[sz].next=head[a];
        head[a]=sz;
        e[sz].to=b;
        sz2++;
        e2[sz2].next=head2[b];
        head2[b]=sz2;
        e2[sz2].to=a;
    }
    void dfs(ll x){
        vis[x]=1;
        for (ll i=head[x];i;i=e[i].next){
            ll v=e[i].to;
            if (!vis[v]) dfs(v);
            s[x]=max(s[x],s[v]+t[v]);
        }
    }
    void dfs2(ll x){
        vis2[x]=1;
        for (ll i=head2[x];i;i=e2[i].next){
            ll v=e2[i].to;
            if (!vis2[v]) dfs2(v);
            d[x]=min(d[x],d[v]-t[x]);
        }
    }
    int main(){
        scanf("%lld%lld",&n,&m);
        for (ll i=1;i<=n;i++) scanf("%lld",&t[i]);
        for (ll i=1;i<=m;i++){
            scanf("%lld%lld",&a,&b);
            insert(b,a);
        }
        for (ll i=1;i<=n;i++) insert(i,0),insert(n+1,i),d[i]=LONG_LONG_MAX;
        dfs(n+1);
        t[n+1]=0,d[n+1]=s[n+1];
        dfs2(0);
        for (ll i=1;i<=n;i++) ans+=d[i]-s[i];
        printf("%lld
    %lld",s[n+1],ans);
    }
    

    B - 国际城市设计大赛

    次小生成树

    树上倍增,维护向上(2^i)个点的祖先(fa),以及过程中的最长边(f1)和次长边(f2)(f1)(f2)的转移方法和吉司机线段树相同

    每次尝试把一条不在MST中的边((i,j,w))加到树中,并删去原来MST中((i,j))路径上的最长边

    由于要求的是严格次小生成树,所以如果最长边长度和(w)相等,就删去原来MST中((i,j))路径上的次长边

    #include <cstdio>
    #include <algorithm>
    #include <climits>
    #include <iostream>
    using namespace std;
    typedef long long ll;
    const ll N=3*1e5+1,K=20;
    ll n,m,a,b,c,sz,head[N],fa[N][K],vis[N],f1[N][K],f2[N][K],d[N],mn,mx,siz,ans=LONG_LONG_MAX;
    struct P{
        ll a,b,w,use;
        P(){}
        P(ll _a,ll _b,ll _w){a=_a,b=_b,w=_w,use=0;}
        friend bool operator<(P a,P b){
            return a.w<b.w;
        }
    }p[N];
    struct E{
        ll next,to,w;
    }e[2*N];
    void insert(ll a,ll b,ll w){
        sz++;
        e[sz].next=head[a];
        head[a]=sz;
        e[sz].to=b;
        e[sz].w=w;
    }
    ll find(ll x){
        return fa[x][0]==x?x:fa[x][0]=find(fa[x][0]);
    }
    void mst(){
        sort(p+1,p+m+1);
        for (ll i=1;i<=n;i++) fa[i][0]=i;
        for (ll i=1;i<=m;i++){
            ll ta=find(p[i].a),tb=find(p[i].b);
            if (ta!=tb){
                p[i].use=1;
                fa[tb][0]=ta;
                siz+=p[i].w;
                insert(p[i].a,p[i].b,p[i].w);
                insert(p[i].b,p[i].a,p[i].w);
            }
        }
    }
    void dfs(ll x){
        for (ll i=1;i<K;i++){
            fa[x][i]=fa[fa[x][i-1]][i-1];
            if (f1[x][i-1]==f1[fa[x][i-1]][i-1]){
                f1[x][i]=f1[x][i-1];
                f2[x][i]=max(f2[x][i-1],f2[fa[x][i-1]][i-1]);
            }else if (f1[x][i-1]>f1[fa[x][i-1]][i-1]){
                f1[x][i]=f1[x][i-1];
                f2[x][i]=max(f2[x][i-1],f1[fa[x][i-1]][i-1]);
            }else{
                f1[x][i]=f1[fa[x][i-1]][i-1];
                f2[x][i]=max(f1[x][i-1],f2[fa[x][i-1]][i-1]);
            }
        }
        for (ll i=head[x];i;i=e[i].next){
            ll v=e[i].to;
            if (!d[v]){
                d[v]=d[x]+1;
                fa[v][0]=x;
                f1[v][0]=e[i].w;
                dfs(v);
            }
        }
    }
    void update(ll x){
        if (x>mx){
            mn=mx;
            mx=x;
        }
        if (mx>x&&x>mn){
            mn=x;
        }
    }
    void lca(ll a,ll b){
        mn=mx=0;
        if (d[a]<d[b]) swap(a,b);
        for (ll i=K-1;i>=0;i--)
            if (d[fa[a][i]]>=d[b]){
                update(f1[a][i]);update(f2[a][i]);
                a=fa[a][i];
            }
        if (a==b) return;
        for (ll i=K-1;i>=0;i--)
            if (fa[a][i]!=fa[b][i])
            {
                update(f1[a][i]);update(f2[a][i]);
                update(f1[b][i]);update(f2[b][i]);
                a=fa[a][i],b=fa[b][i];
            }
        update(f1[a][0]);update(f2[a][0]);
        update(f1[b][0]);update(f2[b][0]);
    }
    void solve(){
        for (ll i=1;i<=m;i++)
            if (!p[i].use){
                lca(p[i].a,p[i].b);
                if (p[i].w!=mx){
                    ans=min(ans,siz+p[i].w-mx);
                }else if (mn!=0){
                    ans=min(ans,siz+p[i].w-mn);
                }
            }
    }
    int main(){
        scanf("%lld%lld",&n,&m);
        for (ll i=1;i<=m;i++){
            scanf("%lld%lld%lld",&a,&b,&c);
            p[i]=P(a,b,c);
        }
        mst();
        d[1]=1;
        fa[1][0]=0;
        dfs(1);
        solve();
        printf("%lld",ans);
    }
    

    C - 宁王我好兄弟

    D - 17 张牌你能秒我

    对不等式两侧取对数,把乘法转化成加法,把不等式限制转化为最短路关系

    (a-bleq x_1\b-cleq x_2\c-aleq x_3)

    三个不等式相加有

    (0leq x_1+x_2+x_3)

    (0>x_1+x_2+x_3)时,即图中道路存在负环,此时约束关系无解

    用Bellman-Ford求最短路,如果整个图被更新了(n)次,则说明图中存在负环

    #include <cstdio>
    #include <cmath>
    #include <cstring>
    const int N=5*1e3+1;
    int n,m,a,b,c,head[N],sz;
    double d,f[N];
    struct E{
        int next,to;
        double w;
    }e[2*N];
    void insert(int a,int b,double w){
        sz++;
        e[sz].next=head[a];
        head[a]=sz;
        e[sz].to=b;
        e[sz].w=w;
    }
    bool bellmanford(){
        for (int i=1;i<=n;i++){
            bool t=0;
            for (int j=1;j<=n;j++)
                for (int k=head[j];k;k=e[k].next){
                    int v=e[k].to;
                    if (f[v]>f[j]+e[k].w){
                        f[v]=f[j]+e[k].w;
                        t=1;
                        if (i==n) return true;
                    }
                }
            if (!t) break;
        }
        return false;
    }
    int main(){
        scanf("%d%d",&n,&m);
        for (int i=1;i<=m;i++){
            scanf("%d%d%d%lf",&a,&b,&c,&d);
            if (a==1) insert(b,c,-log(d));
            if (a==2) insert(c,b,log(d));
            if (a==3) insert(b,c,-log(d)),insert(c,b,log(d));
        }
        if (bellmanford())
            printf("Delicious");
        else
            printf("DEDEDEDEDEDEDEDEDEDEDEDE");
    }
    

    E - 密室逃脱

    对于每一行都建立一个源点,对于每一行都建立一个汇点

    同时在容量上做限制,保证每一行每一列的流量都为(1)

    矩阵中的每个点都与所在行的源点和所在列的汇点连接,设置对应的负费用

    再建一个超级源点和一个超级汇点,用MCMF跑最小费用流即可,每次增广时在残存网络上跑SPFA找最短路即可

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <queue>
    #include <climits>
    using namespace std;
    const int N=102*102;
    int n,sz=1,head[N],s,t,w,pre[N],dis[N],incf[N],vis[N];
    queue<int> que;
    struct E{
        int next,to,w,c;
    }e[N*4];
    void insert(int a,int b,int w,int c){
        sz++;
        e[sz].next=head[a];
        head[a]=sz;
        e[sz].to=b;
        e[sz].w=w;
        e[sz].c=c;
    }
    void flow(int a,int b,int w,int c){
        insert(a,b,w,c);
        insert(b,a,0,-c);
    }
    bool spfa(){
        memset(dis,0x7f,sizeof(dis));
        que.push(s);dis[s]=0;incf[s]=INT_MAX,incf[t]=0;
        while(!que.empty()){
            int x=que.front();que.pop();
            vis[x]=0;
            for (int i=head[x];i;i=e[i].next){
                int v=e[i].to,w=e[i].w,c=e[i].c;
                if (!w||dis[v]<=dis[x]+c) continue;
                dis[v]=dis[x]+c;
                incf[v]=min(w,incf[x]);
                pre[v]=i;
                if (!vis[v]) que.push(v),vis[v]=1;
            }
        }
        return incf[t];
    }
    int maxflow,mincost;
    void update(){
        maxflow+=incf[t];
        for (int i=t;i!=s;i=e[pre[i]^1].to){
            e[pre[i]].w-=incf[t];
            e[pre[i]^1].w+=incf[t];
            mincost+=incf[t]*e[pre[i]].c;
        }
    }
    int main(){
        scanf("%d",&n);
        s=0;t=(n+1)*(n+1);
        for (int i=1;i<=n;i++)
            for (int j=1;j<=n;j++){
                scanf("%d",&w);
                flow(i,i*(n+1)+j,1,-w);
                flow(i*(n+1)+j,j*(n+1),1,0);
            }
        for (int i=1;i<=n;i++){
            flow(s,i,1,0);
            flow(i*(n+1),t,1,0);
        }
        while(spfa()) update();
        printf("%d",-mincost);
    }
    

    F - 酒厂

    最小树形图,朱刘算法

    #include <cstdio>
    #include <cmath>
    #include <cstring>
    #include <climits>
    #include <iostream>
    using namespace std;
    typedef long long ll;
    const ll N=1e3+1,M=1e4+1;
    ll n,m,a,b,c,siz,in[N],pos,u,v,vis[N],id[N],pre[N],sum;
    struct P{
        ll a,b,w;
    }p[M+N];
    void mdst(ll rt,ll n,ll m){
        siz=0;
        while(1){
            for (ll i=0;i<=n;i++) in[i]=LLONG_MAX;
            for (ll i=1;i<=m;i++){
                ll a=p[i].a,b=p[i].b,w=p[i].w;
                if (a!=b&&w<in[b]){
                    in[b]=w;
                    pre[b]=a;
                    if (a==rt) pos=i;
                }
            }
            for (ll i=0;i<=n;i++){
                if (i==rt) continue;
                if (in[i]==LLONG_MAX){
                    siz=LLONG_MAX;
                    return;
                }
            }
            ll cnt=0;
            in[rt]=0;
            memset(id,-1,sizeof(id));
            memset(vis,-1,sizeof(vis));
            for (ll i=0;i<=n;i++){
                siz+=in[i];
                ll b=i;
                while(vis[b]!=i&&id[b]==-1&&b!=rt){
                    vis[b]=i;
                    b=pre[b];
                }
                if (b!=rt&&id[b]==-1){
                    for (ll a=pre[b];a!=b;a=pre[a])
                        id[a]=cnt;
                    id[b]=cnt++;
                }
            }
            if (cnt==0) break;
            for (ll i=0;i<=n;i++)
                if (id[i]==-1) id[i]=cnt++;
            for (ll i=1;i<=m;i++){
                ll a=p[i].a,b=p[i].b,w=p[i].w;
                p[i].a=id[a];
                p[i].b=id[b];
                if (id[a]!=id[b])
                    p[i].w-=in[b];
            }
            n=cnt-1;
            rt=id[rt];
        }
    }
    int main(){
        scanf("%lld%lld",&n,&m);
        for (ll i=1;i<=m;i++){
            scanf("%lld%lld%lld",&a,&b,&c);
            p[i].a=a,p[i].b=b,p[i].w=c;
            sum+=c;
        }
        sum++;
        for (ll i=1;i<=n;i++){
            p[m+i].a=0,p[m+i].b=i,p[m+i].w=sum;
        }
        mdst(0,n,m+n);
        if (siz>=2*sum){
            printf("-1");
        }else{
            printf("%lld %lld",siz-sum,pos-m);
        }
    }
    

    G - Stardust

    2-SAT

    在一个期望不满足的情况下,另外两个期望必须被满足

    ((a,b,c))就转化为了

    ( eg a o b, eg a o c\ eg b o a, eg b o c\ eg c o a, eg c o b)

    对于每个三元关系连六条边即可

    跑一边tarjan判断是否存在(a)( eg a)位于同一强连通分量,即存在(a)( eg a)能够互推的情况,只有这时是不存在可行解的

    #include <cstdio>
    #include <iostream>
    using namespace std;
    const int N=2*(1e5+1);
    int n,m,a,b,c,xa,xb,xc,head[N],sz,bl[N],cnt,ssz,dfn[N],vis[N],low[N],sta[N];
    struct E{
        int next,to;
    }e[3*N];
    void insert(int a,int b){
        sz++;
        e[sz].next=head[a];
        head[a]=sz;
        e[sz].to=b;
    }
    void tarjan(int x){
        dfn[x]=low[x]=++cnt;
        vis[x]=1;
        sta[++ssz]=x;
        for (int i=head[x];i;i=e[i].next){
            int v=e[i].to;
            if (!dfn[v]){
                tarjan(v);
                low[x]=min(low[x],low[v]);
            }else{
                if (vis[v])
                    low[x]=min(low[x],dfn[v]);
            }
        }
        if (dfn[x]==low[x]){
            do{
                bl[sta[ssz]]=x;
                vis[sta[ssz]]=0;
            }while(sta[ssz--]!=x);
        }
    }
    int main(){
        scanf("%d%d",&n,&m);
        for (int i=1;i<=m;i++){
            scanf("%d%d%d%d%d%d",&a,&xa,&b,&xb,&c,&xc);
            insert(a*2+xa^1,b*2+xb);insert(a*2+xa^1,c*2+xc);
            insert(b*2+xb^1,a*2+xa);insert(b*2+xb^1,c*2+xc);
            insert(c*2+xc^1,a*2+xa);insert(c*2+xc^1,b*2+xb);
        }
        for (int i=1;i<=2*n;i++)
            if (!dfn[i])
                tarjan(i);
        bool t=1;
        for (int i=1;i<=n;i++)
            if (bl[i*2]==bl[i*2+1]) t=0;
        if (t)
            printf("YES");
        else
            printf("NO");
    }
    

    H - 快乐水偷运计划

    点对((i,j))产生的贡献

    (W=xotimes iotimes j)

    其中(x)是比较好求,把仙人掌中的每个叶子拆开转化成树,再求树上两点间最短边即可

    考虑枚举所有点对((i,j))复杂度过高,并且(xotimes i)没有很好的方法合并统计

    所以考虑单从(x)入手,按照边的长度从大到小加到途中,这样新加入的边一定是连接两侧点路径上的最短边,对于两侧的点分别用并查集维护即可

    由于位运算中每一位之间是独立的,所以对每一位都开一个对应的并查集,维护连通块所有点编号中(0)(1)数量,这样就可以快速计算每次加边时带来的贡献了

    #include <cstdio>
    #include <climits>
    #include <algorithm>
    using namespace std;
    typedef unsigned long long ll;
    const ll N=1e5+1,M=1.5*N,K=64;
    struct E{
        ll next,to,w;
    }e[M*2];
    struct P{
        ll a,b,w,incir;
        P(){};
        P(ll _a,ll _b,ll _w,ll _incir){a=_a,b=_b,w=_w,incir=_incir;};
        friend bool operator<(P a,P b){
            return a.w<b.w;
        }
    }p[M];
    ll n,m,a,b,c,ans,esz,psz,head[N],ssz,fa[N],vis[N],cnt[N][K][2],insta[N];
    P sta[M];
    void cir(){
        ll st=sta[ssz].b,minw=__UINT64_MAX__,minidx=0;
        for (ll i=ssz;i==ssz||sta[i].b!=st;i--){
            sta[i].incir=1;
            if (sta[i].w<minw){
                minw=sta[i].w;
                minidx=i;
            }
        }
        for (ll i=ssz;i==ssz||sta[i].b!=st;i--){
            if (i!=minidx){
                psz++;
                p[psz]=sta[i];
                p[psz].w+=minw;
            }
        }
    }
    void dfs(ll x){
        vis[x]=1;
        insta[x]=1;
        for (ll i=head[x];i;i=e[i].next){
            ll v=e[i].to;
            ssz++;
            sta[ssz]=P(x,v,e[i].w,0);
            if (!vis[v]){
                vis[v]=1;
                fa[v]=x;
                dfs(v);
                if (!sta[ssz].incir){
                    psz++;
                    p[psz]=sta[ssz];
                }
            }else if (v!=fa[x]&&insta[v]){
                cir();
            }
            ssz--;
        }
        insta[x]=0;
    }
    void insert(ll a,ll b,ll w){
        esz++;
        e[esz].next=head[a];
        head[a]=esz;
        e[esz].to=b;
        e[esz].w=w;
    }
    ll find(ll x){
        return x==fa[x]?x:fa[x]=find(fa[x]);
    }
    void mktree(){
        while(psz){
            ll a=p[psz].a,b=p[psz].b,w=p[psz].w;
            ll faa=find(a),fab=find(b);
            for (ll i=0;i<K;i++){
                for (ll j=0;j<=1;j++){
                    ll t=(w>>i)&1;
                    ans+=cnt[faa][i][j]*cnt[fab][i][j^t^1]*(1<<i);
                }
            }
            fa[fab]=faa;
            for (ll i=0;i<K;i++)
                for (ll j=0;j<=1;j++)
                    cnt[faa][i][j]+=cnt[fab][i][j];
            psz--;
        }
    }
    int main(){
        scanf("%llu%llu",&n,&m);
        for (ll i=1;i<=m;i++){
            scanf("%llu%llu%llu",&a,&b,&c);
            insert(a,b,c);insert(b,a,c);
        }
        sta[0]=P(0,1,0,0);
        dfs(1);
        sort(p+1,p+1+psz);
        for (ll i=1;i<=n;i++){
            fa[i]=i;
            for (ll j=0;j<K;j++)
                cnt[i][j][(i>>j)&1]++;
        }
        mktree();
        printf("%llu",ans);
    }
    

    I - LOL

    Tarjan求割点

    #include <cstdio>
    #include <iostream>
    using namespace std;
    const int N=1e5+1;
    int idx,n,m,a,b,sz,ans,head[N],dfn[N],low[N],cut[N],ch[N];
    struct E{
        int next,to;
    }e[2*N];
    void insert(int a,int b){
        sz++;
        e[sz].next=head[a];
        head[a]=sz;
        e[sz].to=b;
    }
    void tarjan(int x){
        dfn[x]=low[x]=++idx;
        for (int i=head[x];i;i=e[i].next){
            int v=e[i].to;
            if (!dfn[v]){
                tarjan(v);
                if (low[v]>=dfn[x]) cut[x]=1;
                low[x]=min(low[x],low[v]);
                ch[x]++;
            }
            low[x]=min(low[x],dfn[v]);
        }
    }
    int main(){
        scanf("%d%d",&n,&m);
        for (int i=1;i<=m;i++){
            scanf("%d%d",&a,&b);
            insert(a,b);insert(b,a);
        }
        for (int i=1;i<=n;i++){
            if (!dfn[i]){
                tarjan(i);
                cut[i]=(ch[i]>=2);
            }
        }
        for (int i=1;i<=n;i++) ans+=cut[i];
        printf("%d",ans);
    }
    

    J - 荣耀永远属于星辰十字军

    素数=奇数+偶数

    这样所有的边都连接的都是一个奇数和一个偶数,恰好构成了一个二分图

    两部分分别对应超级源点和超级汇点,这样就可以用最大流解决了

    跑一个流量就代表两点间连一条边,看能不能把所有点容量为2的边跑满即可

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <queue>
    #include <climits>
    using namespace std;
    const int N=301,M=20001;
    int n,a[N],sz=1,head[M],s,t,w,pre[M],dis[M],incf[M],vis[M],pri[M],cnt[2];
    queue<int> que;
    struct E{
        int next,to,w,c;
    }e[(N*N+N)*2];
    void insert(int a,int b,int w,int c){
        sz++;
        e[sz].next=head[a];
        head[a]=sz;
        e[sz].to=b;
        e[sz].w=w;
        e[sz].c=c;
    }
    void flow(int a,int b,int w,int c){
        insert(a,b,w,c);
        insert(b,a,0,-c);
    }
    bool spfa(){
        memset(dis,0x7f,sizeof(dis));
        que.push(s);dis[s]=0;incf[s]=INT_MAX,incf[t]=0;
        while(!que.empty()){
            int x=que.front();que.pop();
            vis[x]=0;
            for (int i=head[x];i;i=e[i].next){
                int v=e[i].to,w=e[i].w,c=e[i].c;
                if (!w||dis[v]<=dis[x]+c) continue;
                dis[v]=dis[x]+c;
                incf[v]=min(w,incf[x]);
                pre[v]=i;
                if (!vis[v]) que.push(v),vis[v]=1;
            }
        }
        return incf[t];
    }
    int maxflow,mincost;
    void update(){
        maxflow+=incf[t];
        for (int i=t;i!=s;i=e[pre[i]^1].to){
            e[pre[i]].w-=incf[t];
            e[pre[i]^1].w+=incf[t];
            mincost+=incf[t]*e[pre[i]].c;
        }
    }
    int main(){
        scanf("%d",&n);
        s=0,t=1;
        for (int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            cnt[a[i]%2]++;
            if (a[i]%2){
                flow(a[i],1,2,0);
            }else{
                flow(0,a[i],2,0);
            }
        }
        for (int i=2;i<M;i++) pri[i]=1;
        for (int i=2;i<M;i++)
            if (pri[i])
                for (int j=2*i;j<M;j+=i) pri[j]=0;
        for (int i=1;i<=n;i++)
            for (int j=1;j<=n;j++)
                if (a[i]%2==0&&a[j]%2==1&&pri[a[i]+a[j]]){
                    flow(a[i],a[j],1,0);
                }
        while(spfa()) update();
        if (cnt[0]==cnt[1]&&maxflow==2*cnt[0])
            printf("YES");
        else
            printf("NO");
        
    }
    

    K - Vingying 营救计划

    L - PAFF 的演唱会

    考虑到每个点的门票费,可以转化为该点到超级源点的边长

    求超级源点到每个点的最短路即可

    #include <cstdio>
    #include <queue>
    #include <cstring>
    using namespace std;
    typedef long long ll;
    typedef pair<int,int> P;
    priority_queue<P,vector<P>,greater<P> > que;
    const ll N=2*1e5+1;
    ll n,m,a,b,c,sz,f[N],head[N];
    struct E{
        ll next,to,w;
    }e[4*N];
    void insert(ll a,ll b,ll w){
        sz++;
        e[sz].next=head[a];
        head[a]=sz;
        e[sz].to=b;
        e[sz].w=w;
    }
    void dijkstra(){
        memset(f,0x7f,sizeof(f));
        f[0]=0;
        que.push(P(0,0));
        while(!que.empty()){
            ll x=que.top().second;que.pop();
            for (ll i=head[x];i;i=e[i].next){
                ll v=e[i].to;
                if(f[v]>f[x]+e[i].w){
                    f[v]=f[x]+e[i].w;
                    que.push(P(f[v],v));
                }
            }
        }
    }
    int main(){
        scanf("%lld%lld",&n,&m);
        for (ll i=1;i<=m;i++){
            scanf("%lld%lld%lld",&a,&b,&c);
            insert(a,b,c*2);insert(b,a,c*2);
        }
        for (ll i=1;i<=n;i++){
            scanf("%lld",&a);
            insert(0,i,a);insert(i,0,a);
        }
        dijkstra();
        for (ll i=1;i<=n;i++) printf("%lld ",f[i]);
    }
    

    M - 魔法少女小糖

    N - 法外狂徒

    邻接矩阵快速幂,最短距离矩阵(f),方案数矩阵(g),转移时同时计算(f)(g)

    (f[i][j]=min{f_1[i][k]+f_2[k][j]})

    (g[i][j]=sum_{f[i][j]=f[i][k]+f[k][j]}{g_1[i][k]*g_2[k][j]})

    对于询问(x o y),最短距离为(f[x][y]),方案数为(g[x][y])

    O - 间谍

    P - 矩阵乘法

    欧拉回路

    首先用并查集判断整个图是否连通,如果不连通则无解

    如果所有点的入度和出度相等,就取其中最大的数作为起点和终点

    如果恰有一个点出度=入度+1,同时恰有一个点入度=出度+1,就分别把这两个点作为起点和终点

    #include <cstdio>
    #include <iostream>
    using namespace std;
    const int N=101;
    int n,x,y,a[N],b[N],sig,mx,ga,gb,ans,wrong,fa[N];
    int find(int x){
        return x==fa[x]?x:fa[x]=find(fa[x]);
    }
    int main(){
        scanf("%d",&n);
        for (int i=1;i<N;i++) fa[i]=i;
        for (int i=1;i<=n;i++){
            scanf("%d%d",&x,&y);
            a[x]++;
            b[y]++;
            mx=max(mx,x);
            int faa=find(x),fab=find(y);
            if (faa!=fab){
                fa[fab]=faa;
            }
        }
        for (int i=1;i<N;i++)
            for (int j=1;j<N;j++)
                if (a[i]&&a[j]&&find(i)!=find(j))
                    wrong=1;
        for (int i=1;i<N;i++){
            if (a[i]==b[i]+1) {
                if (ga) wrong=1;
                ga=i;
            }else if (a[i]==b[i]-1) {
                if (gb) wrong=1;
                gb=i;
            }else if (a[i]!=b[i]){
                wrong=1;
            }
        }
        if (ga&&gb){
            ans=ga*gb;
        }else{
            ans=mx*mx;
        }
        if (wrong) ans=-1;
        printf("%d",ans);
    }
    

    Q - 赛马 Ⅱ

    把删点的操作倒着处理,就转化成了加点的操作

    魔改一下Floyd最小环即可

    #include <cstdio>
    #include <climits>
    #include <iostream>
    using namespace std;
    const int N=501,INF=INT_MAX/3;
    int n,m,q,t[N],w[N][N],a,b,c,f[N][N],del[N],ans=INF,lans[N];
    int main(){
        scanf("%d%d%d",&n,&m,&q);
        for (int i=1;i<=n;i++)
            for (int j=1;j<=n;j++)
                w[i][j]=f[i][j]=INF;
        q--;
        for (int i=1;i<=m;i++){
            scanf("%d%d%d",&a,&b,&c);
            f[a][b]=f[b][a]=w[a][b]=w[b][a]=c;
        }
        for (int i=1;i<=q;i++){
            scanf("%d",&a);
            t[i]=a,del[a]=1;
        }
        for (int k=1;k<=n;k++){
            if (!del[k]){
                for (int i=1;i<=n;i++)
                    for (int j=1;j<=n;j++){
                        if (i!=j&&!del[i]&&!del[j]){
                            ans=min(ans,f[i][j]+w[i][k]+w[k][j]);
                        }
                        f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
                    }
            }
        }
        lans[q+1]=ans==INF?-1:ans;
        for (int z=q,k=t[z];z>=1;z--,k=t[z]){
            del[k]=0;
            for (int i=1;i<=n;i++)
                for (int j=1;j<=n;j++){
                        if (i!=j&&!del[i]&&!del[j]){
                            ans=min(ans,f[i][j]+w[i][k]+w[k][j]);
                        }
                        f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
                    }
            lans[z]=ans==INF?-1:ans;
        }
        for (int i=1;i<=q+1;i++) printf("%d
    ",lans[i]);
    }
    

    R - 传送门

  • 相关阅读:
    机器学习:K-近邻分类
    集体智慧编程2:聚类算法(分级聚类和K-均值聚类)(以博客的聚类为例,附有获取数据的代码)
    机器学习简介
    集体智慧编程1:推荐算法(基于协作性过滤collaborative filtering)(实例加代码)
    图片轮播
    A页面调到B页面,B页面关闭时A页面刷新
    css 上下滚动效果
    js数组的sort排序详解
    查看机器上安装的jdk能支持多大内存
    from表单如果未指定action,submit提交时候会执行当前url
  • 原文地址:https://www.cnblogs.com/algonote/p/13085605.html
Copyright © 2011-2022 走看看