2017 UESTC Training for Graph Theory

2017 UESTC Training for Graph Theory

A       思维

题意:给你一个有n个点和m条边的无向连通图,每条边都有一个权值w。我们定义,对于一条路径,它的Charm value为该路径上所有边的权值的最大值与最小值的差。询问从1到n的所有路径的Charm value的最小值。

tags:有点思维定式了。。一条路径里只要最大最小值,所以边可以重复走。这样我们只要把边从小到大枚举,把第 i 条边作为最小边,然后对于每个 i ,我们按顺序逐一加入比它大的边,直到点 1与点 n连通,更新ans即可。

#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define rep(i,a,b) for (int i=a;i<=b;i++)
#define per(i,b,a) for (int i=b;i>=a;i--)
#define mes(a,b)  memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
#define MP make_pair
#define PB push_back
#define fi  first
#define se  second
typedef long long ll;
const int N = 205, M = 1005;

int n, m, fa[N], ans=INF;
pair<int, pair<int ,int >  > e[M];
int Find(int x) { return fa[x]==x ? x : fa[x]=Find(fa[x]); }
void Unite(int u, int v)
{
    int fau=Find(u), fav=Find(v);
    if(fau!=fav) fa[fav]=fau;
}
void solve(int x)
{
    rep(i,1,n) fa[i]=i;
    rep(i,x,m)
    {
        Unite(e[i].se.fi, e[i].se.se);
        if(Find(1)==Find(n)) {
            ans=min(ans, e[i].fi-e[x].fi);
            return ;
        }
    }
}
int main()
{
    scanf("%d %d", &n, &m);
    int u, v, w;
    rep(i,1,m)
    {
        scanf("%d %d %d", &u, &v, &w);
        e[i]=MP(w, MP(u, v));
    }
    sort(e+1, e+1+m);
    rep(i,1,m)
    {
        solve(i);
    }
    printf("%d\n", ans);

    return 0;
}

B       建模转最短路

题意:给你一个大小为n的集合S,集合里有n个互不相同正整数。有q个询问,每次询问是否能选择S中的一些数字 ( 同一个数字可以选择多次,也可以任何数字都不选),使它们相加的和为m。

tags:好难想到取余数,转为最短路建模

选取基点a1,而dis[r]表示除a1余r的最小的数。这样我们可以把所有数都分为a1类,即除a1余 0~(a1-1)。然后初始是dis[0]=0加入队列,松驰操作是对每个ai,看能不能dis[(u+ai)%a1] > dis[u]+ai,即更新最短路。 最后询问的m,只要看dis[m%a1]是否 <=m即可。

#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define rep(i,a,b) for (int i=a;i<=b;i++)
#define per(i,b,a) for (int i=b;i>=a;i--)
#define mes(a,b)  memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
#define inf  1e18
#define MP make_pair
#define PB push_back
#define fi  first
#define se  second
typedef long long ll;
const int N = 500005;

int n, a[2005], q;
ll  m, dis[N];
bool inq[N];
void spfa(int st)
{
    mes(inq, 0);
    mes(dis, 63);  dis[0]=st;
    queue<int > Q;
    Q.push(st);  inq[st]=1;
    while(!Q.empty())
    {
        int  u=Q.front();  Q.pop();
        rep(i,2,n)
        {
            int to=(u+a[i])%a[1];
            if(dis[to] > dis[u]+a[i])
            {
                dis[to] = dis[u]+a[i];
                if(inq[to]==0) inq[to]=1, Q.push(to);
            }
        }
        inq[u]=0;
    }
}
int main()
{
    scanf("%d", &n);
    rep(i,1,n) scanf("%d", &a[i]);
    spfa(0);
    scanf("%d", &q);
    rep(i,1,q)
    {
        scanf("%d", &m);
        if(dis[m%a[1]] <= m) puts("YES");
        else puts("NO");
    }

    return 0;
}

C       dfs 01染色  或  欧拉路径

题意:二维平面上有n个点。你需要将每个点染成红色或蓝色。请设计一种染色方案能够使得每一行和每一列的红色的点的数量与蓝色的点的数量之差都不超过 1。

tags:很难想到思路,还是套路少。。

方法一: 如果某一行或某一列有k个点,那么可以对这k个点连k/2条边,使得每个点至多连了一条边。然后,对全图进行dfs染色,使得每条边两边的点的颜色不相同。

这个感觉比较难想,但好理解,也更好写。。

#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define rep(i,a,b) for (int i=a;i<=b;i++)
#define per(i,b,a) for (int i=b;i>=a;i--)
#define mes(a,b)  memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
#define MP make_pair
#define PB push_back
#define fi  first
#define se  second
#define PII  pair<int , int >
#define all(x)  x.begin(), x.end()
typedef long long ll;
const int N = 200005;

int n;
bool vis[N], ans[N];
vector<PII > X[N], Y[N];
struct Edge { int to, next; } e[N<<2];
int tot, head[N];
void Addedge(int u, int v) {
    e[tot]={v, head[u]}, head[u]=tot++;
    e[tot]={u, head[v]}, head[v]=tot++;
}
void dfs(int u, int flag)
{
    vis[u]=true, ans[u]=flag;
    for(int i=head[u]; i!=-1; i=e[i].next)
    {
        int to=e[i].to;
        if(vis[to]==false)
        {
            dfs(to, flag^1);
        }
    }
}
int main()
{
    mes(head, -1);
    scanf("%d", &n);
    int x, y;
    rep(i,1,n)
    {
        scanf("%d %d", &x, &y);
        X[x].PB(MP(y,i)), Y[y].PB(MP(x,i));
    }
    rep(i,1,N)
    {
        for(int j=1; j<X[i].size(); j+=2) {
            Addedge(X[i][j-1].se, X[i][j].se);
        }
        for(int j=1; j<Y[i].size(); j+=2) {
            Addedge(Y[i][j-1].se, Y[i][j].se);
        }

    }
    rep(i,1,n) if(vis[i]==false) dfs(i, 0);
    rep(i,1,n) putchar(ans[i] ? ‘r‘ : ‘b‘);
    puts("");

    return 0;
}
/*
8
1 1
1 2
2 1
3 3
4 3
3 4
4 4
5 5
*/

方法二:   把每个点看成边,每个横纵坐标看成一个点,得到一个无向图。如果新图中每个点的度都是偶数,那么就是一个欧拉图,对该图跑一遍欧拉回路,对走过的边轮流染色,就可以保证每个点所连的边的红蓝颜色相等。如果存在度数为奇数的点,新建两个点a和b.把横坐标的度数为奇数的点和a连边,把纵坐标为奇数的点和b连边,这样最多只有a和b的度数为奇数,可以跑欧拉路径。

fleury(弗罗莱) 算法,有个好的地方,要及时把访问过的边从图中删去(是真的删去而不是打标记),否则重复访问会导致复杂度飙升。这样就只好用vector存图方便删除。

#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define rep(i,a,b) for (int i=a;i<=b;i++)
#define per(i,b,a) for (int i=b;i>=a;i--)
#define mes(a,b)  memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
#define MP make_pair
#define PB push_back
#define fi  first
#define se  second
#define PII  pair<int, int >
typedef long long ll;
const int N = 200005, M = N<<2;

int n, ans[M];
vector<PII > G[M];
bool vise[M], flag;
void Addedge(int u, int v, int x) {
    G[u].PB(MP(v, x));  G[v].PB(MP(u, x));
}
inline void dfs(int u)
{
    while(!G[u].empty())
    {
        PII e=G[u].back();
        G[u].pop_back();
        int to=e.fi, x=e.se;
        if(vise[x]==false)
        {
            vise[x]=true;
            dfs(to);
            if(0<x && x<=n) ans[x]=flag, flag^=1;
        }
    }
}
void fleury()
{
    int a=0, k=n;
    rep(i,1,N*2)
    {
        if(G[i].size()&1)
        {
            Addedge(a, i, ++k);
        }
    }
    rep(i,0,N*2) if(!G[i].empty())
        dfs(i);
}
int main()
{
    scanf("%d", &n);
    int x, y;
    rep(i,1,n)
    {
        scanf("%d %d", &x, &y);
        y += N;
        Addedge(x, y, i);
    }
    fleury();
    rep(i,1,n) putchar(ans[i] ? ‘r‘ : ‘b‘);
    puts("");

    return 0;
}

I       欧拉回路,dfs

题意:构造一个01串,使其满足以下条件:环状(即首尾相连),每一位取值为0或1,长度是2n。对于每个(2n
个)位置,从其开始沿逆时针方向的连续的n位01串(包括自己)构成的数均不相同,即0到2n−1中的数各出现一次。
例如:n=3,样例的00010111,对于每个位置,沿逆时针方向连续长度为3的01串有:000,001,010,101,011,111,110,100,即为0−7的所有数字。

tags:按照规律连好边,dfs搜索即可。

#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define rep(i,a,b) for (int i=a;i<=b;i++)
#define per(i,b,a) for (int i=b;i>=a;i--)
#define mes(a,b)  memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
#define MP make_pair
#define PB push_back
#define fi  first
#define se  second
typedef long long ll;
const int N = 5000005;

int n, sta[N], top, s;
bool vis[N], flag;
vector<int > G[N];
int ans[N], k;
void dfs(int u)
{
    if(vis[u]==1) return ;
    if(top==s)
    {
        flag=1;   k=top;
        rep(i,0,top-1) ans[i]=sta[i];
        return ;
    }
    vis[u]=1;
    for(auto v : G[u]) if(vis[v]==0)
    {
        if(flag) return;
        sta[top++]=v;
        dfs(v);
        --top;
    }
    vis[u]=0;
}
int main()
{
    scanf("%d", &n);
    s=1<<n;
    rep(i,0,s-1)
    {
        int x=i>>1;
        if(x!=i) G[i].PB(x);
        x+= (1<<(n-1));
        if(x!=i) G[i].PB(x);
    }
    sta[top++]=0;
    dfs(0);
    printf("0");
    rep(i,1,k-1) printf("%d", ans[i]%2);

    return 0;
}

时间: 2024-10-07 23:19:51

2017 UESTC Training for Graph Theory的相关文章

UESTC_方老师和农场 2015 UESTC Training for Graph Theory&lt;Problem L&gt;

L - 方老师和农场 Time Limit: 3000/1000MS (Java/Others)     Memory Limit: 65535/65535KB (Java/Others) Submit Status 方老师重新开了一家农场,农场一共有N个节点和M条双向边,但是有一个很大的问题就是有比如一个农场A到达农场B只有一条路径,问至少添加多少条边使得任意两个农场之间的路径多于一条. Input 多组数据,EOF结束. 第1行:N和M. 第2到第M+1行:每一行2个数Ui和Vi,表示Ui到

UESTC_排名表 2015 UESTC Training for Graph Theory&lt;Problem I&gt;

I - 排名表 Time Limit: 3000/1000MS (Java/Others)     Memory Limit: 65535/65535KB (Java/Others) Submit Status 暑假前集训已经过了一半了,我们将会把当前排名公布出来.但是此刻秋实大哥却心急火燎,因为他不慎把排名删除了. 一共有n个人参加排名,每个人都有一个名次,没有哪两个人的名次是相同的.现在秋实大哥掌握的一些情报,比如Ai的名次要先于Bi.(编号从1开始) 你能帮秋实大哥恢复出排名表吗? Inp

UESTC_传输数据 2015 UESTC Training for Graph Theory&lt;Problem F&gt;

F - 传输数据 Time Limit: 3000/1000MS (Java/Others)     Memory Limit: 65535/65535KB (Java/Others) Submit Status 机房里面有m台电脑,n台网线,每条网线都每秒中最多传送的数据量,现在需要你计算从标号为1的电脑传送数据到编号为m的电脑,问一秒内最多传送多少数据? Input 第1行: 两个用空格分开的整数N(0≤N≤200)和 M(2≤M≤200).N网线的数量,M是电脑的数量. 第二行到第N+1行

UESTC_秋实大哥带我飞 2015 UESTC Training for Graph Theory&lt;Problem B&gt;

B - 秋实大哥带我飞 Time Limit: 300/100MS (Java/Others)     Memory Limit: 65535/65535KB (Java/Others) Submit Status 然而题目和题面并没有什么关系. 给出n个点,m条带权无向边,问你从1号点到n号点的最短路中有多少种走法? Input 第一行两个数n,m分别表示点的个数和边的个数. (2≤n≤2000,1≤m≤2000) 接下来m行,每行3个数u,v,w表示u号点到v号点有一条距离为w的边.(1≤u

UESTC_秋实大哥与连锁快餐店 2015 UESTC Training for Graph Theory&lt;Problem A&gt;

A - 秋实大哥与连锁快餐店 Time Limit: 9000/3000MS (Java/Others)     Memory Limit: 65535/65535KB (Java/Others) Submit Status 成大事者,不惟有超世之才,亦有坚忍不拔之志. 秋实大哥开了一家快餐店之后,由于人赢光环的影响,很快就赚得了大量的资金.为了继续实现心中的远大的理想,他打算在全国各地开设分店赚大钱.假设现在有n家快餐店(其中有至少有一家是旗舰店)分布在二维平面上,第i家快餐店的坐标为(xi,

UESTC_树上的距离 2015 UESTC Training for Graph Theory&lt;Problem E&gt;

E - 树上的距离 Time Limit: 2000/1000MS (Java/Others)     Memory Limit: 262143/262143KB (Java/Others) Submit Status 给你一棵带权树,边权表示两点间的距离.有如下两种操作: 询问两点间的最短距离. 修改一条边权值. 对每个操作1,回答两点间的距离. Input 第一行一个数n,表示点的个数.接下来n−1行,每行3个数u,v,w,表示u,v间有权为w的边.边按照输入顺序从1到n−1编号.输入保证为

UESTC_韩爷的情书 2015 UESTC Training for Graph Theory&lt;Problem H&gt;

H - 韩爷的情书 Time Limit: 6000/2000MS (Java/Others)     Memory Limit: 262144/262144KB (Java/Others) Submit Status 某年某月某日,韩爷被妹子表白了\o/ 同时,韩爷收到了来自妹子的情书.在好奇心的驱使下,众人想要一览究竟. 显然,羞涩韩爷是不会把情书直接拿出来的. 假设情书长度为n+2,韩爷从中提取出n个长度为3的连续字符串,分给了n个人. 现在这n个人向你求助,能否帮他们把情书恢复出来. I

UESTC_秋实大哥与时空漫游 2015 UESTC Training for Graph Theory&lt;Problem C&gt;

C - 秋实大哥与时空漫游 Time Limit: 4500/1500MS (Java/Others)     Memory Limit: 65535/65535KB (Java/Others) Submit Status 秋实大哥通过全国的连锁快餐店发家致富,赚了大钱,接下来他打算通过这些钱实现他的另一个梦想————遨游太空,漫游星际. 秋实大哥满怀期待的出发了. .......啦啦啦啦啦啦啦啦啦...... 最后,秋实大哥花完了钱,觉得是时候回地球继续赚钱和过节了. 但是却被告知回地球的专机

UESTC_小panpan学图论 2015 UESTC Training for Graph Theory&lt;Problem J&gt;

J - 小panpan学图论 Time Limit: 3000/1000MS (Java/Others)     Memory Limit: 65535/65535KB (Java/Others) Submit Status 小panpan不会图论,所以图论专题他非常刻苦地学习图论. 今天他认真地学习了萌神的ppt,学习了一下Floyd算法,手持两把锟斤拷的他, 口中疾呼烫烫烫,马上找了到OJ上找了道FLoyd的题: n个点,m边的无向连通图,无重边,无自环,每条边的长度都是1,求任意两点之间的