新生专题四并查集

一。食物链

食物链

Time Limit: 1000 ms /Memory Limit: 10000 kb

Description

动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形。A吃B, B吃C,C吃A。
现有N个动物,以1-N编号。每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种。
有人用两种说法对这N个动物所构成的食物链关系进行描述:
第一种说法是"1 X Y",表示X和Y是同类。
第二种说法是"2 X Y",表示X吃Y。
此人对N个动物,用上述两种说法,一句接一句地说出K句话,这K句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。
1) 当前的话与前面的某些真的话冲突,就是假话;
2) 当前的话中X或Y比N大,就是假话;
3) 当前的话表示X吃X,就是假话。
你的任务是根据给定的N(1 <= N <= 50,000)和K句话(0 <= K <= 100,000),输出假话的总数。

Input

第一行是两个整数N和K,以一个空格分隔。
以下K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。
若D=1,则表示X和Y是同类。
若D=2,则表示X吃Y。

Output

只有一个整数,表示假话的数目。

Sample Input
100 7
1 101 1 
2 1 2
2 2 3 
2 3 3 
1 1 3 
2 3 1 
1 5 5
Sample Output
 
 
3
这题写过博客。i和j+n是i吃j,i和j+2*n是j吃i。
#include <cstdio>
#define MAXN 50001
#define MAXK 100000

///i-A i-B i-C
int father[MAXN * 3 + 1];
int rak[MAXN * 3 + 1];

void init(int n)
{
    for(int i = 0; i < n; i++){
        father[i] = i;
        rak[i] = 0;
    }
}

int find(int n)
{
    if(n == father[n])
        return n;
    else
        return father[n] = find(father[n]);
}

void unite(int x, int y)
{
    x = find(x);
    y = find(y);

    if(x == y)
        return;

    if(rak[x] < rak[y])
        father[x] = y;
    else{
        father[y] = x;
        if(rak[x] == rak[y])
            rak[x]++;
    }
}

bool same(int x, int y)
{
    return find(x) == find(y);
}

int main()
{
    int n, k, ans;
    int a, b, c;

    scanf("%d%d", &n, &k);

    init(n * 3);

    ans = 0;
    for(int i = 0; i < k; i++)
    {
        scanf("%d%d%d", &a, &b, &c);
        a--;b--;

        if(a < 1 || a > 2 || b < 0 || b >= n || c < 0 || c >= n){
            ans++;
            continue;
        }

        if(a == 1){///如果同类
            if(same(b, c + n) /**b吃c*/|| same(b, c + n * 2/**c吃b*/))
                ans++;
            else{
                unite(b, c);
                unite(b + n, c + n);
                unite(b + 2 * n, c + 2 * n);
            }
        }else{
            if(same(b, c) || same(b, c + 2 * n))
                ans++;
            else{
                unite(b, c + n);
                unite(b + n, c + 2 * n);
                unite(b + 2 * n, c);
            }
        }

    }

    printf("%d", ans);

    return 0;
}
网上有带权并查集的写法。太复杂。
链接: 点击打开链接
这在并查集的节点里存了信息,更新和合并要更新节点的信息,好像是当做向量处理的。

二。Synchronous Design

Synchronous Design

Time Limit: 1000 ms /Memory Limit: 10000 kb

Description

The designers of digital integrated circuits (IC) are very concerned about the correctness of their designs because, unlike software, ICs cannot be easily tested. Real tests are not possible until the design has been finalized and the IC has been produced.
To simulate the behavior of a digital IC and to more or less guarantee that the final chip will work, all of today's digital ICs are based on a synchronous design.


Figure: The critical path (dashed line) takes 43ns to settle

In a synchronous design, an external clock signal triggers the IC to go from a well defined and stable state to the next one. On the active edge of the clock, all input and output signals and all internal nodes are stable in either the high or low state. Between two consecutive edges of the clock, the signals and nodes are allowed to change and may take any intermediate state. The behavior of a synchronous network is predictable and will not fail due to hazards or glitches introduced by irregularities of the real circuit.
To analyze whether an IC has a synchronous design, we distinguish between synchronous and asynchronous nodes. Flip flops are synchronous nodes. On the active edge of the clock, the output of the flip flop changes to the state of the input and holds that state throughout the next clock cycle. Synchronous nodes are connected to the clock signal.
Simple gates like ANDs or ORs are asynchronous nodes. Their output changes - with a short delay - whenever one of their inputs changes. During that transition phase, the output can even go into some undefined or intermediate state.
For simplicity, we assume that all inputs of the circuits are directly connected to the output of a synchronous node outside the circuit and that all outputs of the circuit are directly connected to the input of a synchronous node outside the circuit.
For an IC to have a synchronous design, mainly two requirements must be met:

  • The signal delay introduced between two synchronous nodes must be smaller or equal than the clock period so there is enough time for nodes to become stable. In figure 1, the round-ended boxes are asynchronous nodes whereas the square boxes are synchronous nodes. The delay introduced on the dashed path is 43ns and exceeds the given clock period of 30ns.
  • There may be n o cycles composed exclusively of asynchronous nodes. In the real circuit such cycles could oscillate. In figure 2, the dashed path constitutes a cycle of asynchronous nodes.

Figure 3 shows a circuit with a synchronous design. It does not contain cycles composed of asynchronous nodes and the longest path between two synchronous nodes is shorter than the clock period of 30ns.

Figure: The design contains a cycle (dashed line)


Figure: A synchronous design

Your are to write a program that decides for a given IC whether it has a synchronous design or not. You are given a network of synchronous and asynchronous nodes, a delay for each node, some inputs and outputs and the clock period.
You may safely assume that

  • the delays introduced between any input and any output of the same node are equal, i.e. equal to the delay given for that node,
  • synchronous nodes have no delay at all,
  • all connections between two nodes connect an output to an input.

Input

The input file contains several circuits. The first line gives the number of circuits in the file.
For each circuit in the file, the first line contains the clock period for the circuit, given as an integer number in nanoseconds. The next line gives the number of nodes. The following lines each contain a node, described by a letter and a integer number. The letter is 'i' for an input, 'o' for an output, 'a' for an asynchronous node and 's' for a synchronous node. The number gives the delay introduced by the node as an integer number in nanoseconds (only meaningful for an asynchronous node). Nodes are implicitly numbered, starting at zero.
After the nodes, the number of connections for the circuit follows. Each following line contains a pair of integer numbers denoting a connection between the output and the input of two nodes. The connection links an output of the node given by the first number and an input of the node given by the second number.
The clock signal is not given in the input file. We assume that all synchronous nodes are properly connected to the clock signal.

Output

For each circuit in the input file, your output file should contain a line with one of the following messages:


  • "Synchronous design. Maximum delay: < ss >." if the circuit has a synchronous design.
  • < ss > should be replaced by the longest delay found on any path between two synchronous nodes.
  • "Circuit contains cycle." if the circuit contains a cycle composed exclusively of asynchronous nodes.
  • "Clock period exceeded." if there is a path between two synchronous nodes that is longer than the given clock period and there are no cycles composed of asynchronous nodes.

Sample Input
1
30
10
i 0
i 0
i 0
i 0
o 0
o 0
a 9
a 11
a 8
s 0
9
0 8
1 7
2 6
2 6
6 7
7 8
8 4
7 9
9 5
Sample Output
Synchronous design. Maximum delay: 28.

读题目就去掉半条命。
太难不会。
WA的代码。。
那啥,样例过了就算过了,WA'是oj有问题,恩就是这样。
我觉得这是我考虑并查集时没考虑方向,就像食物链一样,要么多开几个空间表示方向,要么用向量偏移量(我没太看懂这个写法)。
懒的写了,变大佬了再来吧。
#include <cstdio>
#include <map>
#include <utility>
using namespace std;
#define MAXN 10000

struct node{
  char c;
  int f;
  int w;
};
node par[MAXN];
int rak[MAXN];
bool flag;
map <int, int> p;
int no[MAXN], k, na[MAXN], l;

void init(int n)
{
    for(int i = 0; i < n; i++){
        par[i].f = i;
        rak[i] = 0;
    }
}

int find(int x)
{
    if(par[x].f == x)
        return x;
    else{
        return par[x].f = find(par[x].f);
    }
}

bool isa(int x)
{
    bool f = false;
    for(int i = 0; i < l; i++)
    if(find(i) == x){
        f = true;
        break;
    }
    return f;
}

void unite(int x, int y)
{
    x = find(x);
    y = find(y);

    if(x == y){
        if(isa(x))
            flag = true;
        return;
    }

    if(rak[x] < rak[y])
        par[x].f = y;
    else{
        par[y].f = x;
        if(rak[x] == rak[y])
            rak[x]++;
    }
}

bool same(int x, int y)
{
    return find(x) == find(y);
}

int solve()
{
    int ans = 0;
    int os[MAXN], m = 0;

    for(int i = 0; i < k; i++){
        bool f = false;
        for(int j = 0; j < m; j++){
            if(same(no[i], os[j])){
                f = true;
                break;
            }
        }
        if(!f) os[m++] = no[i];
    }

    //for(int i = 0; i < m; i++)
      //  printf("mi %d %d %c\n", i, os[i], par[i].c);

    for(int i = 0; i < m; i++){
        int cnt = 0;
        for(int j = 0; j < l; j++){
            if(same(os[i], na[j]))
                cnt += par[na[j]].w;
        }
        if(cnt > ans)
            ans = cnt;
        //printf("cnt %d\n", cnt);
    }

    return ans;
}

int main()
{
    int n, maxt, nnode, nedge, cnt;

    scanf("%d", &n);

    while(n--)
    {
        flag = false;
        scanf("%d%d", &maxt, &nnode);

        init(nnode);
        k = l = 0;
        p.clear();
        for(int i = 0; i < nnode; i++){
            scanf(" %c%d", &par[i].c, &par[i].w);
            if(par[i].c == 'o')
                no[k++] = i;
            else if(par[i].c == 'a')
                na[l++] = i;
        }

        scanf("%d", &nedge);
        for(int i = 0; i < nedge; i++){
            int a, b;
            scanf("%d%d", &a, &b);
            if(p.find(a)->second != b && p.find(b)->second != a){
                p.insert(pair<int, int>(a, b));
                p.insert(pair<int, int>(b, a));
                unite(a, b);
            }
        }

        if(!flag)
            cnt = solve();

        if(flag){
            printf("Circuit contains cycle.\n");
        }else if(cnt <= maxt){
            printf("Synchronous design. Maximum delay: < %d >.\n", cnt);
        }else{
            printf("Clock period exceeded.\n");
        }
    }

    return 0;
}
/**
2
30
10
i 0
i 0
i 0
i 0
o 0
o 0
a 9
a 11
a 8
s 0
9
0 8
1 7
2 6
2 6
6 7
7 8
8 4
7 9
9 5
30
10
i 0
i 0
i 0
i 0
o 0
o 0
a 9
a 11
a 8
s 0
9
0 8
1 7
2 6
2 6
6 7
7 8
8 4
7 9
9 5
*/
/**
2
10
7
i 0
i 0
i 0
o 0
a 3
a 3
a 3
7
0 4
4 3
1 5
2 6
6 5
4 6
5 4
30
10
i 0
i 0
i 0
i 0
o 0
o 0
a 9
a 11
a 8
s 0
9
0 8
1 7
2 6
2 6
6 7
7 8
8 4
7 9
9 5
*/
/*
2
10
7
i 0
i 0
i 0
o 0
a 20
a 3
a 3
6
0 4
4 3
1 5
2 6
6 5
5 4
30
10
i 0
i 0
i 0
i 0
o 0
o 0
a 9
a 11
a 8
s 0
9
0 8
1 7
2 6
2 6
6 7
7 8
8 4
7 9
9 5
*/
/**
1
16
6
i 0
a 7
a 8
a 1
s 0
o 0
6
0 1
1 2
1 3
3 4
4 5
2 3
*/

三。Microfiches

太难不会。和并查集啥关系。

四。Freckles

Freckles

Time Limit: 1000 ms /Memory Limit: 65536 kb

Description

In an episode of the Dick Van Dyke show, little Richie connects the freckles on his Dad's back to form a picture of the Liberty Bell. Alas, one of the freckles turns out to be a scar, so his Ripley's engagement falls through.
Consider Dick's back to be a plane with freckles at various (x,y) locations. Your job is to tell Richie how to connect the dots so as to minimize the amount of ink used. Richie connects the dots by drawing straight lines between pairs, possibly lifting the pen between lines. When Richie is done there must be a sequence of connected lines from any freckle to any other freckle.

Input

The first line contains 0 < n <= 100, the number of freckles on Dick's back. For each freckle, a line follows; each following line contains two real numbers indicating the (x,y) coordinates of the freckle.

Output

Your program prints a single real number to two decimal places: the minimum total length of ink lines that can connect all the freckles.

Sample Input
3
1.0 1.0
2.0 2.0
2.0 4.0
Sample Output
3.41
最小生成树的题目,放在比并查集里是想让我们用Kruskal,单是这种稠密图prim更适合一点。
我两种算法都写了。
g++的double输出要%f。。。。。。
//#include <bits/stdc++.h>
#include <cstdio>
#include <utility>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
typedef pair<double, double> P;
#define MAXN 105
#define INF 10000000
bool vis[MAXN];
double lowc[MAXN];
int n;
P pots[MAXN];
double maps[MAXN][MAXN];

double prim()
{
    double ans = 0;
    memset(vis, false, sizeof vis);
    vis[1] = true;

    for(int i = 1; i <= n; i++)
        lowc[i] = maps[1][i];

    for(int i = 2; i <= n; i++){
        double minc = INF;
        int p = -1;
        for(int j = 1; j <= n; j++)
            if(!vis[j] && minc > lowc[j])
        {
            minc = lowc[j];
            p = j;
        }

        if(minc >= INF)
            return -1;

        ans += minc;

        vis[p] = true;

        for(int j = 1; j <= n; j++){
            if(!vis[j] && lowc[j] > maps[p][j])
                lowc[j] = maps[p][j];
        }
    }

    return ans;
}

int main()
{
    scanf("%d", &n);

        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= n; j++)
                maps[i][j] = INF;

        for(int i = 1; i <= n; i++){
            double x, y;

            scanf("%lf%lf", &x, &y);
            pots[i].first = x;
            pots[i].second = y;

            for(int j = 1; j < i; j++){
                double dx = fabs(x - pots[j].first);
                double dy = fabs(y - pots[j].second);
                maps[i][j] = maps[j][i] = sqrt(dx*dx + dy*dy);
            }
        }

        printf("%.2f\n", prim());


    return 0;
}

/*
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<cmath>
using namespace std;

const int N = 105;

struct node{
   double x, y;
}nodes[N];
struct Edge{
  int u, v;
  double w;
}edge[N * (N - 1) / 2 + 1];
int tol = 0;

int par[N + 1];

void addedge(int i, int j)
{
    double dx = fabs(nodes[i].x - nodes[j].x);
    double dy = fabs(nodes[i].y - nodes[j].y);

    edge[tol].u = i;
    edge[tol].v = j;
    edge[tol++].w = sqrt(dx * dx + dy * dy);
}

bool cmp(Edge a, Edge b)
{
    return a.w < b.w;
}

int findroot(int x)
{
    if(par[x] == -1) return x;
    else return par[x] = findroot(par[x]);
}

double Kruskal(int n)
{
    memset(par, -1, sizeof(par));
    sort(edge, edge + tol, cmp);

    int cnt = 0;
    double ans = 0;
    for(int i = 0; i < tol; i++){

        int u = edge[i].u, v = edge[i].v;
        double w = edge[i].w;
        int t1 = findroot(u);
        int t2 = findroot(v);

        if(t1 != t2){
            ans += w;
            par[t1] = t2;
            cnt++;
        }

        if(cnt == n - 1)
            break;
    }

    if(cnt < n - 1)
        return -1;
    else
        return ans;
}

int main()
{
    int n;

    scanf("%d", &n);

    for(int i = 0; i < n; i++)
    {
        scanf("%lf%lf", &nodes[i].x, &nodes[i].y);

        for(int j = 0; j < i; j++){
            addedge(i, j);
        }
        //printf("%lf %lf\n", nodes[i].x, nodes[i].y);
    }

    // c++ : printf("%.2lf\n", Kruskal(n));
    // g++ : printf("%.2f\n", Kruskal(n));
    printf("%.2f\n", Kruskal(n));

    return 0;
}
*/


五。Cube Stacking

Cube Stacking

Time Limit: 2000 ms /Memory Limit: 30000 kb

Description

Farmer John and Betsy are playing a game with N (1 <= N <= 30,000)identical cubes labeled 1 through N. They start with N stacks, each containing a single cube. Farmer John asks Betsy to perform P (1<= P <= 100,000) operation. There are two types of operations:
moves and counts.
* In a move operation, Farmer John asks Bessie to move the stack containing cube X on top of the stack containing cube Y.
* In a count operation, Farmer John asks Bessie to count the number of cubes on the stack with cube X that are under the cube X and report that value.

Write a program that can verify the results of the game.

Input

* Line 1: A single integer, P

* Lines 2..P+1: Each of these lines describes a legal operation. Line 2 describes the first operation, etc. Each line begins with a 'M' for a move operation or a 'C' for a count operation. For move operations, the line also contains two integers: X and Y.For count operations, the line also contains a single integer: X.

Note that the value for N does not appear in the input file. No move operation will request a move a stack onto itself.

Output

Print the output from each of the count operations in the same order as the input file.

Sample Input
6
M 1 6
C 1
M 2 4
M 2 6
C 3
C 4
Sample Output
1
0
2
这题要求查询节点到根节点的距离,是带权并查集。每个节点对应两个值,一个是到根节点的距离dis,一个是自己的集合的元素个数cnt,也就是一个方块自己下面和自己所处的stack有多高。
所以进行move操作时,移动的集合和被堆放的集合需要改变集合元素个数,移动的集合需要改变到根节点距离。
但是可以在move (x ,y)时把y的cnt加到x的dis上而不改变x的cnt,并把x直接接到y的根节点上,这样路径压缩时只需要更新dis,并且深度就两层。
也就是说,直接接到根节点,并且根节点存整个集合元素的个数。
dis更新可以在路径压缩时完成,路径压缩会把一个节点直接指向根节点,递归过程中又正好会遍历那条路。move操作只需要改变原集合的根节点x, y的值,以后find时会自动完成计算更新x集合的dis。

#include <cstdio>
#define MAXN 30000
#define MAXP 100000

int par[MAXN + 1];
int dis[MAXN + 1], cnt[MAXN + 1];

int find(int x)
{
    if(par[x] != x){
        int temp = par[x];///在路径压缩前记录
        par[x] = find(par[x]);///压缩过程中更新了dis[temp]的值。
        dis[x] += dis[temp];///更新
    }
    //printf("%d %d\n", x, dis[x]);
    return par[x];
}

int unite(int x, int y)
{
    x = find(x);
    y = find(y);

    par[x] = y;
    ///加上被压的堆的数量
    dis[x] += cnt[y];
    ///下面堆的数量自增
    ///上面的不用,因为dis已经存了cnt的信息
    cnt[y] += cnt[x];
}

bool same(int x, int y)
{
    return find(x) == find(y);
}

int main()
{
    int n, m;

    scanf("%d", &n);m = n;

    for(int i = 1; i <= MAXN; i++){
        par[i] = i;
        cnt[i] = 1;
        dis[i] = 0;
    }

    while(n--)
    {
        char c;
        int x, y, goal;

        scanf(" %c", &c);

        if(c == 'M'){
            scanf("%d%d", &x, &y);
            if(!same(x, y))
                unite(x, y);
        }else{
            scanf("%d", &goal);
            find(goal);
            printf("%d\n", dis[goal]);
        }
    }

    return 0;
}

六。The Suspects

The Suspects

Time Limit: 1000 ms /Memory Limit: 20000 kb

Description

Severe acute respiratory syndrome (SARS), an atypical pneumonia of unknown aetiology, was recognized as a global threat in mid-March 2003. To minimize transmission to others, the best strategy is to separate the suspects from others.
In the Not-Spreading-Your-Sickness University (NSYSU), there are many student groups. Students in the same group intercommunicate with each other frequently, and a student may join several groups. To prevent the possible transmissions of SARS, the NSYSU collects the member lists of all student groups, and makes the following rule in their standard operation procedure (SOP).
Once a member in a group is a suspect, all members in the group are suspects.
However, they find that it is not easy to identify all the suspects when a student is recognized as a suspect. Your job is to write a program which finds all the suspects.

Input

The input file contains several cases. Each test case begins with two integers n and m in a line, where n is the number of students, and m is the number of groups. You may assume that 0 < n <= 30000 and 0 <= m <= 500. Every student is numbered by a unique integer between 0 and n−1, and initially student 0 is recognized as a suspect in all the cases. This line is followed by m member lists of the groups, one line per group. Each line begins with an integer k by itself representing the number of members in the group. Following the number of members, there are k integers representing the students in this group. All the integers in a line are separated by at least one space.
A case with n = 0 and m = 0 indicates the end of the input, and need not be processed.

Output

For each case, output the number of suspects in one line.

Sample Input
100 4
2 1 2
5 10 13 11 12 14
2 0 1
2 99 2
200 2
1 5
5 1 2 3 4 5
1 0
0 0
Sample Output
4
1
1
水题,直接用板子,最后记个数就好。

#include <cstdio>
#define N 30001
#define M 501

int par[N];
int rak[N];

void init(int n)
{
    for(int i = 0; i < n; i++){
        par[i] = i;
        rak[i] = 0;
    }
}

int find(int x)
{
    if(x == par[x])
        return x;
    else
        return par[x] = find(par[x]);
}

void unite(int x, int y)
{
    x = find(x);
    y = find(y);

    if(x == y)
        return;

    if(rak[x] < rak[y])
        par[x] = y;
    else{
        par[y] = x;
        if(rak[x] == rak[y])
            rak[x]++;
    }
}

bool same(int x, int y)
{
    return find(x) == find(y);
}

int main()
{
    int n, m;

    while(scanf("%d%d", &n, &m) != EOF && n)
    {
        int ans = 1;

        init(n);

        for(int i = 0; i < m; i++){
            int times, t, s;
            scanf("%d", ×);
            if(times)
                scanf("%d", &t);
            for(int i = 1; i < times; i++){
                scanf("%d", &s);
                unite(s, t);
            }
        }

        for(int i = 1; i < n; i++)
            if(same(0, i))
                ans++;

        printf("%d\n", ans);
    }

    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值