• 11.24ACM补题


    CodeForces 989C

    #include<iostream>
    #include<stdio.h>
    #include<string.h>
    using namespace std;
    int a,b,c,d;
    char mapp[51][51];
    int main(){
        scanf("%d%d%d%d",&a,&b,&c,&d);
        a--,b--;
        for(int i=1;i<=25;i++)
        for(int j=1;j<=50;j++)
        mapp[i][j]='a';
        for(int i=26;i<=50;i++)
        for(int j=1;j<=50;j++)
        mapp[i][j]='b';
        for(int i=1;i<=25;i+=2)
        for(int j=1;j<=50;j+=2)
        if(b)mapp[i][j]='b',b--;
        else if(c)mapp[i][j]='c',c--;
        else if(d)mapp[i][j]='d',d--;
        for(int i=27;i<=50;i+=2)
        for(int j=1;j<=50;j+=2)
        if(a)mapp[i][j]='a',a--;
        else if(c)mapp[i][j]='c',c--;
        else if(d)mapp[i][j]='d',d--;
        puts("50 50");
        for(int i=1;i<=50;i++){
            for(int j=1;j<=50;j++)
            cout<<mapp[i][j];
            cout<<endl;
        }
        return 0;
    }
    View Code

    这就是其实很简单但就是想不出来的题目

    如果考虑n和m的大小的话,我没有思路,一开始也因为这个放弃了

    但其实题目没有要求n和m的大小,所以全部取最大50就行了

    然后先分成上下ab两部分,然后在不改变原来连通块的基础上增加a,b,c,d的连通个数

    需要注意一点就是——如果1到25行全部染成a,在26到50行增加a的连通时要从27开始。毕竟26染成a后可能和25行连通了

     CodeForces - 489D

    #include<iostream>
    #include<stdio.h>
    #include<string.h>
    using namespace std;
    const int maxn=3007;
    struct edge{
        int to,next;
    }e[maxn*10];
    int n,m,head[maxn],a,b,tot,d[maxn],c[maxn][maxn];
    void add(int u,int v){
        e[tot].next=head[u];
        e[tot].to=v;
        head[u]=tot++;
    }
    void init(){
        c[0][0]=1;
        for(int i=1;i<maxn;i++){
            c[i][0]=1;
            for(int j=1;j<=i;j++)
            c[i][j]=c[i-1][j]+c[i-1][j-1];
        }
    }
    int main(){
        init();
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        head[i]=-1;
        while(m--){
            scanf("%d%d",&a,&b);
            add(a,b);
        }
        int ans=0;
        for(int u=1;u<=n;u++){
            for(int i=head[u];i!=-1;i=e[i].next){
                int v=e[i].to;
                for(int j=head[v];j!=-1;j=e[j].next){
                    int vv=e[j].to;
                    if(vv==u)continue;
                    d[vv]++;
                }
            }
            for(int i=1;i<=n;i++)
            if(d[i]){
                ans+=c[d[i]][2];
                d[i]=0;
            }
        }
        printf("%d
    ",ans);
        return 0;
    }
    View Code

    比较简单的图论

    起点u通过两个中转点v1,v2到达终点vv,问这种情况有多少种

    其实就是遍历1到N作为起点,然后记录达到vv的个数,然后能找到多少种两条,答案就能增加多少

     CodeForces - 437C 

    #include<iostream>
    #include<stdio.h>
    #include<string.h>
    using namespace std;
    const int maxn=1007;
    struct edge{
        int to,next,fro;
    }e[maxn*4];
    int n,m,head[maxn],a,b,tot,v[maxn];
    bool vis[maxn];
    void add(int u,int v){
        e[tot].next=head[u];
        e[tot].to=v;
        e[tot].fro=u;
        head[u]=tot++;
    }
    int main(){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        scanf("%d",&v[i]);
        while(m--){
            scanf("%d%d",&a,&b);
            add(a,b),add(b,a);
        }
        int ans=0;
        for(int i=0;i<tot;i++)
        ans+=min(v[e[i].to],v[e[i].fro]);
        printf("%d
    ",ans/2);
        return 0;
    }
    View Code

    同样很简单的图论

    分析一下就能看出——剪每条边都必须要增加边两端其中一个顶点的值

    而考虑剪法,其实就是选择两个顶点中值较小的那个

    所以遍历一下边,增加两个顶点中值最小的就行了

     CodeForces - 500B 

    #include<iostream>
    #include<stdio.h>
    #include<string.h>
    using namespace std;
    const int maxn=307;
    int f[maxn],n,p[maxn],pos[maxn],ans[maxn];
    char ch;
    int get(int x){
        if(f[x]==x)return x;
        else return f[x]=get(f[x]);
    }
    void join(int a,int b){
        int fa=get(a),fb=get(b);
        if(fa==fb)return;
        f[fa]=fb;
    }
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d",&p[i]);
            pos[p[i]]=i;
            f[i]=i;
        }
        for(int i=1;i<=n;i++){
            getchar();
            for(int j=1;j<=n;j++){
                ch=getchar();
                if(ch=='1')join(i,j);
            }
        }
        for(int i=1;i<=n;i++){
            int posi=pos[i];
            for(int j=1;j<=n;j++)
            if(!ans[j]&&get(j)==get(posi)){
                ans[j]=i;
                break;
            }
        }
        for(int i=1;i<=n;i++)
        cout<<ans[i]<<' ';
        cout<<endl;
        return 0;
    }
    View Code

    这也不算图论的题吧,就是将能转移的位置并在一起

    然后让小的数尽量移动到前面去

    CodeForces - 1039C

    #include<stdio.h>
    #include<iostream>
    #include<string.h>
    #include<algorithm>
    #include<set>
    using namespace std;
    typedef long long LL;
    const int maxn=500007;
    const int mod=1e9+7;
    int n,m,k,sz,num,f[maxn];
    LL val[maxn],ans,t;
    set<int>st;
    struct edge{
        int u,v;
        LL w;
        bool operator<(const edge &a)const{
            return w<a.w;
        }
    }e[maxn];
    int get(int x){
        if(f[x]==x)return x;
        else return f[x]=get(f[x]);
    }
    void join(int a,int b){
        int fa=get(a),fb=get(b);
        if(fa==fb)return;
        f[fa]=fb;
        st.insert(a);st.insert(b);
        sz--;
    }
    LL qow(LL a,int b){
        LL ans=1;
        while(b){
            if(b&1)
                ans=(ans*a)%mod;
            a=(a*a)%mod;
            b>>=1;
        }
        return ans;
    }
    int main(){
        scanf("%d%d%d",&n,&m,&k);
        for(int i=1;i<=n;i++)
            scanf("%lld",&val[i]);
        for(int i=0;i<m;i++){
            scanf("%d%d",&e[i].u,&e[i].v);
            e[i].w=val[e[i].u]^val[e[i].v];
        }
        sort(e,e+m);
        t=-1;
        for(int i=1;i<=n;i++)
            f[i]=i;
        for(int i=0;i<m;)
        if(e[i].w!=t){
            t=e[i].w;
            num++;sz=n;
            while(e[i].w==t){
                join(e[i].u,e[i].v);
                i++;
            }
            ans+=qow(2,sz);
            ans%=mod;
            for(auto it:st)f[it]=it;
            st.clear();
        }
        ans+=((qow(2,k)+mod-num)%mod)*(qow(2,n)%mod);
        ans%=mod;
        printf("%lld
    ",ans);
        return 0;
    }
    View Code

    这道题题目又长,异或又不熟,还有卡时间,是道很不友好的题

    但是仔细分析后还是能做出来的

    毕竟一个人总是喜欢把问题想复杂,这样他就可以放弃思考

    而这道题,本质上也就是并查集加快速幂而已

    如果你非要死磕建图,最后只会越想越复杂

    所以做这道题学到的一点就是——当你怎么也想不出来的时候,最好换其他思路,不然会被饶进死圈子

    然而即便我做过这道题,知道做法,还是TLE了几次

    第一次TLE——恢复并起来的点的时候,不要直接1-n遍历恢复,而是将并过的点记录下来,然后恢复

    第二次TLE——记录有多少个连通块的时候,从1-n找f[i]==i要找n次,而并过一次将sz--就比n次少很多,最后两个方法都能找到有多少连通块,不过第二种省时间能过

    CodeForces - 875F

    #include<stdio.h>
    #include<iostream>
    #include<string.h>
    #include<algorithm>
    using namespace std;
    typedef long long LL;
    const int maxn=2e5+7;
    int n,m,f[maxn],ans;
    bool cir[maxn];
    struct Node{
        int a,b,v;
        bool operator<(const Node &a)const{
            return v>a.v;
        }
    }node[maxn];
    int get(int x){
        if(f[x]==x)return x;
        else return f[x]=get(f[x]);
    }
    int main(){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)
            scanf("%d%d%d",&node[i].a,&node[i].b,&node[i].v);
        sort(node+1,node+1+m);
        for(int i=1;i<=n;i++)
            f[i]=i;
        for(int i=1;i<=m;i++){
            int fa=get(node[i].a),fb=get(node[i].b);
            if(fa!=fb){
                if(cir[fa]&&cir[fb])continue;
                ans+=node[i].v;
                f[fa]=fb;
                cir[fb]=cir[fb]|cir[fa];
            }
            else if(!cir[fa]){
                ans+=node[i].v;
                cir[fa]=true;
            }
        }
        printf("%d
    ",ans);
        return 0;
    }
    View Code

    啊这道题我也被骗了。

    想着每个公主能选两个王子,不就是个容量为2的二分图匹配吗

    如果是想找最多的找到王子的公主的个数的话,这个思路没错。但是公主有权值,要让权值最大,这个思路就不行了

    然后再想会不会是什么 带权二分图匹配,不过网上查了没有这种做法

    所以看了题解明白了这又是一道并查集的题


    如果把公主i喜欢的王子ai,bi并在一起。那么一个非环连通块的点个数=边个数+1,表示n个公主在这n+1个王子中选到了n个王子。而如果是一个环,边数=点数,表示n个公主恰好分完了这n个王子。所以我们要做的,就是贪心+并查集。从权值大的公主开始遍历,如果她的两个王子在一个环里,那么显然她的两个王子已经被权值更大的公主选了。

    但也有些需要注意的细节:

    如果fa!=fb代表公主选的两个王子在不同的并查集里,但不代表她一定可以选一个,万一两个并查集都成环了呢.

    同时fa!=fb,但其中一个已经成环,一个没有成环,两个并在一起,同样边数=点数,所以如果让f[fa]=fb,就让fb标记成环

    CodeForces - 350B

    #include<stdio.h>
    #include<iostream>
    #include<string.h>
    #include<vector>
    using namespace std;
    const int maxn=1e5+7;
    struct edge{
        int to,next;
    }e[maxn];
    int n,m,tot,head[maxn],d[maxn],pre[maxn],ans,maxlen,len;
    vector<int>b;
    void add(int u,int v){
        e[tot].to=v;
        e[tot].next=head[u];
        head[u]=tot++;
    }
    void solve(int u,int &len){
        for(int i=head[u];i!=-1;i=e[i].next){
            int v=e[i].to;
            if(d[v]>1)continue;
            pre[u]=v;
            len++;
            solve(v,len);
        }
    }
    void print(int id){
        if(id==0)return;
        print(pre[id]);
        cout<<id<<' ';
    }
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d",&m);
            if(m==1)b.push_back(i);
        }
        for(int i=1;i<=n;i++)
            head[i]=-1;
        for(int i=1;i<=n;i++){
            scanf("%d",&m);
            if(m!=0)add(i,m),d[m]++;
        }
        for(int i=0;i<b.size();i++){
            int u=b[i];
            len=1;
            solve(u,len);
            if(len>maxlen){
                maxlen=len;
                ans=u;
            }
        }
        cout<<maxlen<<endl;
        print(ans);
        return 0;
    }
    View Code

    这道题就比较简单了,要求从起点到终点过程中,不能有一个点能通向两个点。

    所以可以事先记录每个点的出度。

    然后将终点都记录下来,遍历终点找出最长的路径

  • 相关阅读:
    SVN迁移到GIT
    Android之高效率截图
    Android TV 开发(5)
    Android 标题栏(2)
    Android 标题栏(1)
    C# 之抽象类和抽象方法
    浏览器渲染原理及流程
    C#中的DateTime
    由于 Web 服务器上的“ISAPI 和 CGI 限制”列表设置,无法提供您请求的页面。
    配置错误:不能在此路径中使用此配置节。如果在父级别上锁定了该节,便会出现这种情况。锁定是默认设置的
  • 原文地址:https://www.cnblogs.com/helman/p/11962082.html
Copyright © 2020-2023  润新知