第三次模拟赛 订正题解

这次考试是一个图论专练,从海亮回来图论已经搞了好几个月了,感觉有些问题的思想还是要好好揣摩一下,理解思路才能知道怎么做;

第一题:

第一遍没看见有向图,建了个图跑了个tarjan,后来发现读错题,我们来看一下谜一样的水数据,这O(N*M)的复杂都能过;

随便写一个dfs吧,当前点遍历到的点的个数+1,如果遍历N个点,那么满足条件,记录下来,没什么可说的了;

然后数据没让我失望,A了;

#include<bits/stdc++.h>
using namespace std;
#define N 500010
template<typename T>inline void read(T &x)
{
    x=0;T f=1,ch=getchar();
    while(!isdigit(ch))  {if(ch=='-')  f=-1;  ch=getchar();}
    while(isdigit(ch))  {x=(x<<1)+(x<<3)+(ch^48);  ch=getchar();}
    x*=f;
}

int n,m,x,y,tot,cnt,num,root,dfn[N],low[N],ans[N],v[N],lin[N];

struct gg {
    int y,next;
}a[N<<1];

inline void add(int x,int y) {
    a[++tot].y=y;
    a[tot].next=lin[x];
    lin[x]=tot;
}

void dfs(int x) {
    for(int i=lin[x];i;i=a[i].next) 
        if(!v[a[i].y])
            ++cnt,v[a[i].y]=1,dfs(a[i].y);
}

int main() {
    freopen("flow.in","r",stdin);
    freopen("flow.out","w",stdout);
    read(n); read(m);
    for(int i=1,a,b;i<=m;++i)
        read(a),read(b),add(a, b);
    int k=0;
    for(int i=1;i<=n;++i) {
        memset(v,0,sizeof(v)); 
        cnt=1,v[i]=1;
        dfs(i); 
        if(cnt==n) ans[++k]=i;
    }
    if(!k) {
        putchar('0'); 
        return 0;
     }
    printf("%d\n",k);
    for(int i=1;i<=k;++i) printf("%d ",ans[i]);
    return 0;
}
View Code

第二题:

我们来简化一下题意哈:

每次选择两个点异或起来,再删除一个直至剩一个点,并且保证异或和最大;

刚拿到题:以为是个trie树,正准备写,可是我不会怎么处理每次删除哪一个会更优;

后来想了想,是个最大生成树,然后每两个点之间的权值是异或值,跑一个Prim或者Kruskal,考场上sb的推翻自己以为是基于点,我非得写Prim;

数据还是没让人失望,让我这个sbA了;

那没事,我现在补齐两种做法;

Prim:

#include<bits/stdc++.h>
using namespace std;
#define N 2500
#define ll long long
template<typename T>inline void read(T &x)
{
    x=0;T f=1,ch=getchar();
    while(!isdigit(ch))  {if(ch=='-')  f=-1;  ch=getchar();}
    while(isdigit(ch))  {x=(x<<1)+(x<<3)+(ch^48);  ch=getchar();}
    x*=f;
}
ll ans=0;
int n,a[N],Map[N][N],dis[N],vis[N];
inline void Prim_X() {
    vis[1]=1;
    for(int i=1;i<=n;i++) dis[i]=Map[1][i];
    for(int i=1;i<n;i++) {
        int y=0;
        for(int j=1;j<=n;j++) 
            if(!vis[j]&&dis[j]>dis[y])
                y=j;
        ans+=dis[y];
        vis[y]=1;    
        for(int j=1;j<=n;j++) {
            if(!vis[j]) {
                dis[j]=max(dis[j],Map[y][j]);
            }
        }
    }
}

int main() {
    freopen("bull.in","r",stdin);
    freopen("bull.out","w",stdout);
    read(n);
    for(int i=1;i<=n;i++) 
        read(a[i]);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++) 
            Map[i][j]=a[i]^a[j];
    Prim_X();
    cout<<ans<<endl;
    return 0;
}
View Code

Kruskal:

#include<bits/stdc++.h>
using namespace std;
#define N 50010
int n,k,tot,h[N],father[N];
long long ans;
template<typename T>inline void read(T &x)
{
    x=0;T f=1,ch=getchar();
    while(!isdigit(ch))  {if(ch=='-')  f=-1;  ch=getchar();}
    while(isdigit(ch))  {x=(x<<1)+(x<<3)+(ch^48);  ch=getchar();}
    x*=f;
}

struct gg {
    int x,y;
    long long v;
}a[N<<1];

inline int find(int x) {
    return father[x]==x?x:father[x]=find(father[x]);
}

bool mycmp(gg x,gg y) {
    return x.v>y.v;
}

int main() {
    read(n);
    for(int i=1;i<=n;i++) {
        father[i]=i; read(h[i]);
        for(int j=1;j<i;j++) {
            a[++tot].x=i;
            a[tot].y=j;
            a[tot].v=h[i]^h[j];
        }
    }
    sort(a+1,a+tot+1,mycmp);
    k=0;
    for(int i=1;i<=tot;i++) {
        int x=find(a[i].x),y=find(a[i].y);
        if(x==y) continue;
        father[y]=x;
        ans+=a[i].v;
        k++;
        if(k==n-1) break;
    }
    cout<<ans<<endl;
    return 0;
}
View Code

第三题:

re了一个点,? 。跑N边spfa的人都过了,;

我们往常写的都是一对多的spfa,那么今天写一个多对一的spfa,反向建图;

对于每个点我们要求出这个点到点X再回来的最短路,

点X到这个点的最短路只需要跑一遍以点X为源点的最短路就行了,

但是每个点到点X的最短路怎么求呢?把边反向之后再跑一遍以点X为源点的最短路就行了。

代码;考场写的代码太丑了,所以又写了一遍;

#include<bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f
#define N 50010
template<typename T>inline void read(T &x)
{
    x=0;T f=1,ch=getchar();
    while(!isdigit(ch))  {if(ch=='-')  f=-1;  ch=getchar();}
    while(isdigit(ch))  {x=(x<<1)+(x<<3)+(ch^48);  ch=getchar();}
    x*=f;
}

int n,m,s,tot,cnt,a,b,l,dis1[N],dis2[N],lin1[N],lin2[N],vis[N];

struct gg {
    int x,y,v,next;
}e1[N<<1],e2[N<<1];

inline void add1(int x,int y,int v) {
    e1[++tot].y=y;
    e1[tot].next=lin1[x];
    e1[tot].v=v;
    lin1[x]=tot;
}

inline void add2(int x,int y,int v) {
    e2[++cnt].y=y;
    e2[cnt].next=lin2[x];
    e2[cnt].v=v;
    lin2[x]=cnt;
}

inline void spfa1() {
    memset(vis,0,sizeof(vis));
    queue<int> q;
    q.push(s);
    vis[s]=1;dis1[s]=0;
    while(!q.empty()) {
        int x=q.front();
        q.pop();
        vis[x]=0;
        for(int i=lin1[x];i!=0;i=e1[i].next) {
            if(dis1[x]+e1[i].v<dis1[e1[i].y]) {
                dis1[e1[i].y]=dis1[x]+e1[i].v;
                if(!vis[e1[i].y]) {
                    vis[e1[i].y]=1;
                    q.push(e1[i].y);
                }
            }
        }
    }
}

inline void spfa2() {
    queue<int> q;
    memset(vis,0,sizeof(vis));
    q.push(s);
    vis[s]=1;dis2[s]=0;
    while(!q.empty()) {
        int x=q.front();
        q.pop();
        vis[x]=0;
        for(int i=lin2[x];i!=0;i=e2[i].next) {
            
            if(dis2[x]+e2[i].v<dis2[e2[i].y]) {
                dis2[e2[i].y]=dis2[x]+e2[i].v;
                if(!vis[e2[i].y]) {
                    vis[e2[i].y]=1;
                    q.push(e2[i].y);
                }
            }
        }
    }
}

int main() {
    read(n); read(m); read(s);
    memset(dis1,127,sizeof(dis1));
    memset(dis2,127,sizeof(dis2));
    for(int i=1;i<=m;i++) {
        read(a);read(b);read(l);
        e1[i].y=b; e1[i].next=lin1[a]; lin1[a]=i; e1[i].v=l;
        e2[i].y=a; e2[i].next=lin2[b]; e2[i].v=l; lin2[b]=i;//反向建边; 
    }
    spfa1(); spfa2();
    int ans=0;
    for(int i=1;i<=n;i++)
        ans=max(ans,dis1[i]+dis2[i]);
    cout<<ans<<endl;
    return 0;
}
View Code

分数:290分,另外10分献给sb的自己;

本来计划最近复习图论,结果直接考了,最近计划复习图论考点,写写以前的经典题目,还要学搜索最近,加油!

posted @ 2019-06-17 09:04  Tyouchie  阅读(214)  评论(1编辑  收藏  举报