2017暑期集训 Day 3 搜索与并查集

来源:互联网 发布:剑灵24人本优化不卡 编辑:程序博客网 时间:2024/05/15 15:21

A - 食物链

[solution]
并查集新操作!
维护决策的矛盾信息,每次决策之间的选择是互相矛盾的
每个动物可能为三种情况:A、B、C,i代表第i种动物是A类动物,i+n代表第i种动物是B类,i+2*n是c类,这样
对于d=1时,x、y为同类动物,即如果x是a类,则y也为a类,即unite(x,y),同理,都为b类,unite(x+n,y +n),同理,unite(x+2*n,y+2*n)
对于d=2时,x吃y,如果x是a类,则y是b类,即unite(x, y + n),同理unite(x + n, y + 2*n), unite(x +2*n, y),下面考虑矛盾的情况,对于d=1时,x、y会有九种状态,出去最后操作的三种状态,另外六种状态都是不允许出现的,即(x, y), (x + n, y + n), (x + 2n, y +2n), (x, y + 2n),(x + n, y), (x + 2n, y + n)难道我们必须打6个if吗?我们观察这六种状态,前三种是等价的,真假值相同,这样我们每次判断两个即可
d=2类似

[Code]

#include<cstdio>#include<iostream>#include<set>using namespace std;const int N = 200000 + 500;int f[N];int n, m;int find(int x){    if (f[x] == x)        return x;    return f[x] = find(f[x]);}void unio(int x, int y){    int xx = find(x);    int yy = find(y);    if (xx != yy)        f[xx] = yy;}bool cal(int x, int y){    int xx = find(x);    int yy = find(y);    return xx == yy;}int main(){   // freopen("a.in", "r", stdin);    scanf("%d%d", &n, &m);    for(int i = 1; i <= n * 3; i++)        f[i] = i;    int ans = 0;    for(int i = 1; i <= m; i++)    {        int d, x, y;        scanf("%d%d%d", &d, &x, &y);        if (x < 1 || x > n || y < 1 || y > n)        {            ans++;            continue;        }        if (d == 1)        {            if (cal(x, y + n) || cal(x, y + 2 * n))            {                ans++;                continue;            }            unio(x, y);            unio(x + n, y + n);            unio(x + 2 * n, y + 2 * n);        }        else        {            if (x == y)            {                ans++;                continue;            }            if (cal(x, y) || cal(x, y + 2 * n))            {                ans++;                continue;            }            unio(x, y + n);            unio(x + n, y + 2 * n);            unio(x + 2 * n, y);        }    }    printf("%d", ans);    return 0;}

B - Wireless Network

[Solution]
一个朴素的二分
[Code]

#include<cstdio>#include<iostream>using namespace std;const int N= 200000;int f[N];int n, m;bool rep[N];double x[N], y[N];int find(int x){    if (f[x] == x)        return x;    return f[x] = find(f[x]);}int union1(int x, int y){   // printf("%d %d\n", x, y);    int xx = find(x), yy = find(y);    if (xx != yy)        f[xx] = yy;}int main(){   // freopen("a.in", "r", stdin);    int n;    double d;    scanf("%d", &n);    scanf("%lf", &d);    for(int i = 1; i <= n; i++)        scanf("%lf%lf", &x[i], &y[i]);    for(int i = 1; i <= n; i++)        f[i] = i;    string s;    while(cin>>s)    {        if (s == "O")        {            int k;            scanf("%d", &k);            for(int i = 1; i <= n; i++)                if (k != i)            {                rep[k] = true;                double dis = (x[i] - x[k]) * (x[i] - x[k]) + (y[i] - y[k]) * (y[i] - y[k]);                if (rep[i] && dis <= d * d)                    union1 (i, k);            }        }        else        {            int i, j;            scanf("%d%d", &i, &j);            int ii = find(i);            int jj = find(j);            if (ii == jj)                printf("SUCCESS\n");            else                printf("FAIL\n");        }    }    return 0;}

C - The Door Problem

[Solution]
这个思路和A类似,对于每个门都对应两份switch,每个switch有两种状态,这样我们对于每个门进行分析,如果此门的状态是锁住的,我们这两个开关分别是一开一关,因此unite(x,y +n)
|unite(x+n,y), 反之则同时开启或同时关闭
[Code]

#include<cstdio>#include<iostream>using namespace std;const int N = 200000 + 500;int f[N];int a[N][5], n, m, tot[N];int find(int x){    if (f[x] == x)        return x;    return f[x] = find(f[x]);}void unio(int x, int y){    int xx = find(x);    int yy = find(y);    if (xx != yy)        f[xx] = yy;}bool cal(int x, int y){    int xx = find(x);    int yy = find(y);    return xx == yy;}int main(){   // freopen("a.in", "r", stdin);    scanf("%d%d", &n, &m);    for(int i = 1; i <= n; i++)        scanf("%d", &tot[i]);    for(int i = 1; i <= 2 * m; i++)        f[i] = i;    for(int i = 1; i <= m; i++)    {        int top;        scanf("%d", &top);        for(int j = 1; j <= top; j++)        {            int y;            scanf("%d", &y);            if (a[y][0] != 0)                a[y][1] = i;            else                a[y][0] = i;        }    }    for(int i = 1; i <= n; i++)    {        int x = a[i][0], y = a[i][1];        if (tot[i] == 0)        {            if (cal(x, y) || cal(x + m, y + m))            {                printf("NO");                return 0;            }            unio(x, y + m);            unio(x + m, y);        }        else        {            if (cal(x, y + m) || cal(x + m, y))            {                printf("NO");                return 0;            }            unio(x, y);            unio(x + m, y + m);        }    }    printf("YES");    return 0;}

D - 小希的迷宫

[Solution]
判断一副图是不是树,此题坑点很多。。。
首先很容易想到每次加边的时候判断两端点所处的集合是否相同,,,但是最后你还得确保此图联通,所以判断一下点的个数,与边的个数相比较一下即可
注意00的情况
[Code]

#include<cstdio>#include<iostream>#include<set>using namespace std;const int N = 200000 + 500;int f[N];int s[N], t[N], top;set<int> point;int find(int x){    if (f[x] == x)        return x;    return f[x] = find(f[x]);}void unio(int x, int y){    int xx = find(x);    int yy = find(y);    if (xx != yy)        f[xx] = yy;}bool cal(int x, int y){    int xx = find(x);    int yy = find(y);    return xx == yy;}bool calc(){    for(int i = 1; i <= top; i++)    {        int x = s[i], y = t[i];        if (cal(x, y))            {         //        printf("This is %d %d \n", x, y);                return false;            }        if (point.count(x) == 0)            point.insert(x);        if (point.count(y) == 0)            point.insert(y);        unio(x, y);    }    if (point.size() != top + 1)        return false;    return true;}int main(){   // freopen("a.in", "r", stdin);    int x, y;    top = 0;    while(~scanf("%d%d", &x, &y))    {        if (x == -1 && y == -1)            break;        if (x != 0)        {            top++;            s[top] = x;            t[top] = y;        }        else        {            if (top == 0)                {                    printf("Yes\n");                    continue;                }            for(int i = 1; i <= 100000; i++)                f[i] = i;            if (calc())                printf("Yes\n");            else                printf("No\n");            top = 0;            point.clear();        }    }    return 0;}

E - Island Puzzle

[Solution]
我们注意到每次只能把数字移动到0的位置,这样这些数字之间的相对顺序是不会改变的,这样我们读取的时候忽略掉0,把a串写两遍,判断b串是否是a串的一个子串即可
[Code]

#include<cstdio>#include<iostream>using namespace std;const int N= 400000 + 500;int a[N], b[N];int n;bool calc(int l, int r){    int x = l, y = 1;    for(int i = 1; i <= n; i++)    {        if (a[x] != b[y])            return false;        x++; y++;    }    return true;}int main(){    //freopen("a.in", "r", stdin);    scanf("%d", &n);    int top = 0;    for(int i = 1; i <= n; i++)    {       int x;       scanf("%d", &x);       if (x != 0)          a[++top] = x;    }    top = 0;    for(int i = 1; i <= n; i++)    {       int x;       scanf("%d", &x);       if (x != 0)          b[++top] = x;    }    n--;    for(int i = 1; i <= n; i++)        a[i + n] = a[i];    for(int i = 1; i <= n; i++)        if (a[i] == b[1])    {        if (calc(i, i + n - 1))            printf("YES");        else            printf("NO");        return 0;    }    return 0;}

F - The Tag Game

[Solution]
很有意思的一道题目,显然,最后相遇到叶子节点,我们跑一边树形dp,预处理每个结点到以此节点为根的子树的最大路径f[i],这样我们跑一遍第一个人能到达的结点i,ans=max(f[i] + dis[i]),disp[i]为根到i的距离
[Code]

#include<cstdio>#include<iostream>#include<queue>#include<vector>using namespace std;const int N= 400000 + 500;int dis[N], dp[N], par[N];bool f[N];vector<int> a[N];vector<int> tree[N];int n, m;void build(int x){    for(int i = 0; i < a[x].size(); i++)    {        int y = a[x][i];        if (f[y])            continue;        tree[x].push_back(y);        par[y] = x;        f[y] = true;        build(y);    }}void treedp(int x){    if (a[x].size() == 0)    {        dp[x] = 0;        return ;    }    for(int i = 0; i < tree[x].size(); i++)    {        int y = tree[x][i];        treedp(y);        dp[x] = max(dp[x], dp[y] + 1);    }}int main(){   // freopen("a.in", "r", stdin);    scanf("%d%d", &n, &m);    for(int i = 1; i < n; i++)    {        int x, y;        scanf("%d%d", &x, &y);        a[x].push_back(y);        a[y].push_back(x);    }    f[1] = true;    build(1);    treedp(1);    int x = m;    int ans = 0;    while(x != 1)    {        ans++;        x = par[x];    }    int tot = -1, answer = 0;    x = m;    while(x != 1)    {        tot++;        if (tot >= ans - tot)            break;        answer = max(answer, ans - tot + dp[x]);      //*  printf("%d\n", x);        x = par[x];    }    printf("%d", answer * 2);    return 0;}

G - Mike and Shortcuts

[Solution]
很裸的bfs
[Code]

#include<cstdio>#include<iostream>#include<queue>using namespace std;const int N= 400000 + 500;int a[N], dis[N], tle[100];int n, s, t;bool f[N];queue<int >q;int main(){    //freopen("a.in", "r", stdin);    scanf("%d", &n);    for(int i = 1; i <= n; i++)        scanf("%d", &a[i]);    for(int i = 1; i <= n; i++)        dis[i] = n + 250;    dis[1] = 0;    q.push(1);    f[1] = true;    while(!q.empty())    {        int x = q.front();        q.pop();        tle[1] = x - 1;        tle[2] = x + 1;        tle[3] = a[x];        for(int i = 1; i <= 3; i++)        {            int y = tle[i];            if (y < 1 || y > n)                continue;            if (!f[y] && dis[y] > dis[x] + 1)            {                dis[y] = dis[x] + 1;             //   printf("%d -- >  %d    %d\n", x, y, dis[y]);                f[y] = true;                q.push(y);            }        }    }    printf("0");    for(int i = 2; i <= n; i++)        printf(" %d", dis[i]);    return 0;}

H - A strange lift

[Solution] dp 搞一下,用时间作为顺序即可
[Code]

#include<cstdio>#include<iostream>using namespace std;const int N= 400000 + 500;int f[N], a[N];int n, s, t;int main(){    //freopen("a.in", "r", stdin);    while(~scanf("%d", &n) && n)    {        scanf("%d%d", &s, &t);        for(int i = 1; i <= n ; i++)            scanf("%d", &a[i]);        for(int i = 1; i <= n; i++)            f[i] = -1;        f[s] = 0;        for(int i = 1; i <= 300; i++)        {            bool delta = false;            for(int j = 1; j <= n; j++)                if (f[j] >= 0)            {                int x = j - a[j];                if (x >= 1 && (f[x] == -1 || f[x] > f[j] + 1))                    {                        delta = true;                        f[x] = f[j] + 1;                    }                x = j + a[j];                if (x <= n && (f[x] == -1 || f[x] > f[j] + 1))                    {                        delta = true;                        f[x] = f[j] + 1;                    }            }            if (!delta)                break;        }        printf("%d\n", f[t]);    }    return 0;}

总结

今天get到并查集的新操作,下午做题状态不是很好,但是晚上自己在一个陌生的环境里,状态好的出奇,昨晚打的CF掉分了,。。。D题竟然爆掉了int,  难过希望自己在暑期集训收货更大吧
原创粉丝点击