UESTC 数据结构

老头马桶枪!

Time Limit: 1000 MS     Memory Limit: 64 MB

众所周知,集训队的题目是非常困难的。因此,队员们在挂机AK之后,常常会玩一些游戏。

这次,率先AK的周大爷想出了一个叫老头马桶枪的游戏。

在一个小岛上,有三个物种,一共N个生物生活在一起,他们分别是老头、马桶和枪。他们之间的关系是相互克制的,就像包剪锤一样。老头克制马桶,马桶克制枪,枪又克制老头。

现在,集训队的三名成员,小周,小钱和小胡按次序(周、钱、胡)轮流给出信息,信息有两种形式:

第一种记录方式是1 X Y

,表示 X Y

是同类。

第二种记录方式是2 X Y

,表示 X Y有攻击性行为( X克制 Y

)。

当其中一人给出的信息和之前的人给出的信息矛盾时,他便输了,要请吃晚饭。那么,聪明的你能帮助他们看出谁会输呢?(最多只会有一个人输,当多条信息矛盾时,最先给出矛盾信息的人输)

Input

第一行包含两个整数N

M N是生物总个数, M是三个队员给出的信息总个数。以下 M行,每行包含一句队员的话。(依次是小周、小钱、小胡所说的话,三人轮流给出信息)。形式有 1 x y 2 x y 1N100000 1M100000 1X,YN

Output

输出一行,表示最先给出矛盾信息的人是谁。若是小周,输出1;若是小钱,输出2;若是小胡,输出3;若没有人给出矛盾信息,输出-1

扫描二维码关注公众号,回复: 1613840 查看本文章

Sample input and output

Sample Input Sample Output
100 7
1 100 1 
2 1 2
2 2 3 
2 3 3 
1 1 3 
2 3 1 
1 5 5
1

Source

2018 UESTC Training for Data Structures

UESTC Online Judge

Copyright (C) 2012 - 2018 Ruins He(@ruinshe), Jianjin Fan(@pfctgeorge) and Yun Li(@mzry1992). Project home

Any Problem, Please Report On Issues Page.

这一题仿照poj1182的食物链https://blog.csdn.net/keepcoral/article/details/79946060

食物链循环,a->b,b->c,c->a,这里学到一种新的方法,上面写的实在有点难懂,特别是推到关系那里,真的很难想,这里从别人博客上学到了新方法。

首先,有n个动物,那么我们开辟3n个集合,x,x+n,x+2n,分别表示与x同类,被x吃的,吃x的集合,所以两个动物x和y下面有两种情况:

1 x和y是同类,那么和y同类的即等价于和x同类,吃y的等价于吃x的,被y吃的等价于被x吃的,所以这三个集合要同时合并,

             Union(x,y);
             Union(x+n,y+n);
             Union(x+2*n,y+2*n)

判断真假条件:x被y吃||x吃y,那么直接表示就是x在y+n的集合||x在y+2n的集合,这里y+n代表被y吃的集合,y+2n代表吃y的集合,所以判断条件为judge(x,y+n)&&judge(x,y+2n),

2 x吃y,那么被y吃的等价于吃x的,和y同类的等价于被x吃的,吃y的等价于x的同类,所以集合为

                Union(x,y+n);
                Union(x+n,y+2*n);
                Union(x+2*n,y);

判断真假条件:x和y同类||y吃x,所以条件为 judge(x,y)&&judge(x,y+n)(我的代码可能会有点出入,因为n和2*n我写的恰好相反的)

这里的合并是最关键的操作,下面的几道题也是一样,也就是当不知道情况的时候才可以去合并,这是题目的要求

#include<iostream>
#include<string>
#include<vector>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<set>
#include<stack>
#include<queue>
#include<string>
#include<map>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
int father[300009];
int Find(int x)
{
    if(x!=father[x]) father[x]=Find(father[x]);
    return father[x];
}
int Union(int x,int y)
{
    int i=Find(x);
    int j=Find(y);
    if(i!=j) father[i]=j;
}
int judge(int x,int y)
{
    int i=Find(x);
    int j=Find(y);
    if(i!=j)//不同类
    {
        return 1;
    }
    else return 0;
}
int main()
{
    int n,m,i,j,k;
    scanf("%d%d",&n,&m);
    for(i=1;i<=3*n;i++) father[i]=i;
    for(i=1; i<=m; i++)
    {
        int op,x,y;
        scanf("%d%d%d",&op,&x,&y);
        if(op==1)//x和y是同类,所以判断不同类
        {
            if(judge(x,y+n)&&judge(x,y+2*n))
            {
                Union(x,y);
                Union(x+n,y+n);
                Union(x+2*n,y+2*n);
            }
            else
            {
                if(i%3!=0)printf("%d\n",i%3);
                else printf("3\n");
                break;
            }
        }
        else//x吃y,要判断同类或者y吃x
        {
            if(judge(x,y)&&judge(x,y+2*n))
            {
                Union(x,y+n);
                Union(x+n,y+2*n);
                Union(x+2*n,y);
            }
            else
            {
                if(i%3!=0)printf("%d\n",i%3);
                else printf("3\n");
                break;
            }
        }
    }
    if(i==m+1) printf("-1\n");
    return 0;
}

I - 不如把并查集加上个计数功能吧

Time Limit: 1000 MS     Memory Limit: 64 MB

在另一个宇宙,一个月有 N

天。多变的天气条件使得人们很恼火,终于,天气统计局产生了。它会对外发布 M 条信息,格式如下: X Y 表示第 X 天的天气和第 Y

天一样。

但民众并不满足于此,他们想知道有多少天的天气和第 X

天一样。现在,作为一个聪明的程序员,你能帮他们解决这个问题吗?

Input

第一行包含两个整数N

M N是总天数, M是信息总个数。接下来的 M行,每行一条信息。格式为 x y。然后接下来的一行是一个整数 Q,表示询问总个数接下来的 Q行,每行包含一个数字 z,表示询问有多少天的天气和第 z天一样。 1N100000 1x,y,zN

Output

对于每一个询问,输出一行,包含一个整数,表示和z

天气一样的天数。

Sample input and output

Sample Input Sample Output
10 5
1 2
2 3
3 4
1 5
6 7
2
1
6
5
2

UESTC Online Judge

Copyright (C) 2012 - 2018 Ruins He(@ruinshe), Jianjin Fan(@pfctgeorge) and Yun Li(@mzry1992). Project home

Any Problem, Please Report On Issues Page.

简单并查集,加一个sum数组合并计数就可以了
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
typedef unsigned long long ll;
#define N 100009
#define M 400090
#define C 18446744073709551611
int father[N];
int sum[N];
int n,m;
int Find(int x)
{
    if(father[x]!=x) father[x]=Find(father[x]);
    return father[x];
}
void Union(int x,int y)
{
    int i=Find(x);
    int j=Find(y);
    if(i!=j)
    {
        father[i]=j;
        sum[j]+=sum[i];
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        father[i]=i;
        sum[i]=1;
    }
    while(m--)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        Union(x,y);
    }

    for(int i=1;i<=n;i++) Find(i);
    scanf("%d",&m);
    while(m--)
    {
        int x;
        scanf("%d",&x);
        printf("%d\n",sum[father[x]]);
    }
}

K - 爱吃瓜的伊卡洛斯(1)

Time Limit: 1000 MS     Memory Limit: 64 MB

伊卡洛斯很爱吃西瓜。一次,他来到一个西瓜摊旁,发现水果摊有N个西瓜,但只有红色和黄色两种颜色。

伊卡洛斯很想知道知道一些信息,便于老板交谈了起来。

当老板的话的第一个字符为”A”时,老板会告诉伊卡洛斯一些信息,格式如下:

A x y 1 这句话表示第x个西瓜和第y个西瓜是同一种颜色的。

A x y 2 这句话表示第x个西瓜和第y个西瓜是不同种颜色的。

当然,为了考验伊卡洛斯有没有认真听, 老板也会时不时问伊卡洛斯一些问题,格式如下:

Q x y 这句话表示询问第x个西瓜和第y个西瓜是不是同一种颜色,如果确定为同一种颜色,伊卡洛斯需要回答1;确定为不同种颜色,伊卡洛斯需要回答2;无法确定时伊卡洛斯回答3

注意,伊卡洛斯是根据已获得的信息来回答的。也就是只有这个问题之前的信息才为已知信息。

老板说,只有回答对他全部的问题,伊卡洛斯才能吃到瓜,他聪明的想到了让你来帮助他。

Input

第一行包含两个整数NMN是西瓜总数,M是以AQ开头的老板的话总和。
以下M行,每行包含一条老板的话。形式有A x y 1A x y 2Q x y1N100000 1M200000 1X,YN数据保证没有矛盾

Output

对于每一条$Q$指令,输出1/2/3代表两个西瓜颜色的关系。

Sample input and output

Sample Input Sample Output
6 9  
A 1 2 1  
A 1 3 1  
A 1 4 2  
Q 2 4  
Q 1 6  
A 3 6 1  
A 4 5 2  
Q 1 5  
Q 1 6
2
3
1 
1

Hint

西瓜的颜色有且仅有两种!



对立面的并查集,x表示同色的,x+n表示和x不同颜色的,所以如果说了不同颜色,那么Union(x,y),Union(x+n,y+n) 如果不同颜色,那么Union(x,y+n),Union(x+n,y),这里简单来说就是x和y不同颜色,那么x属于和y不同颜色的集合(y+n)这里。查询的时候,如果Find(x)==Find(y),同色;如果Find(x)=Find(y+n)||Find(y)=Find(x+n),不同色;其它不清楚
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
typedef unsigned long long ll;
#define N 100009
#define M 400090
#define C 18446744073709551611
int father[5*N];
int sum[N];
int n,m;
int Find(int x)
{
    if(father[x]!=x) father[x]=Find(father[x]);
    return father[x];
}
void Union(int x,int y)
{
    int i=Find(x);
    int j=Find(y);
    if(i!=j)
    {
        father[i]=j;
    }
}
int main()
{
    char s[10];
    scanf("%d%d",&n,&m);
    for(int i=1; i<=2*n; i++)
    {
        father[i]=i;
    }
    while(m--)
    {
        int x,y,flag;
        scanf("%s%d%d",&s,&x,&y);
        if(s[0]=='A')
        {
            scanf("%d",&flag);
            if(flag==1)
            {
                Union(x,y);
                Union(x+n,y+n);
            }
            else if(flag==2)
            {
                Union(x,y+n);
                Union(y,x+n);
            }
        }
        else
        {
            if(Find(x)==Find(y)) printf("1\n");
            else if(Find(x)==Find(y+n)||Find(y)==Find(x+n)) printf("2\n");
            else printf("3\n");
        }
    }

}

L - 爱吃瓜的伊卡洛斯(2)

Time Limit: 1000 MS     Memory Limit: 64 MB

伊卡洛斯很爱吃西瓜。一次,他来到一个西瓜摊旁,发现水果摊有$N$个西瓜,西瓜有红色、黄色、绿色、蓝色……等等数不清的颜色。伊卡洛斯很想知道知道一些信息,便于老板交谈了起来。当老板的话的第一个字符为”A”时,老板会告诉伊卡洛斯一些信息,格式如下:$A\ x\ y\ 1$ 这句话表示第$x$个西瓜和第$y$个西瓜是同一种颜色的。$A\ x\ y\ 2$ 这句话表示第$x$个西瓜和第$y$个西瓜是不同种颜色的。

当然,为了考验伊卡洛斯有没有认真听, 老板也会时不时问伊卡洛斯一些问题,格式如下:$Q\ x\ y$ 这句话表示询问第$x$个西瓜和第$y$个西瓜是不是同一种颜色,如果确定为同一种颜色,伊卡洛斯需要回答1;确定为不同种颜色,伊卡洛斯需要回答2;无法确定时伊卡洛斯回答3。注意,伊卡洛斯是根据已获得的信息来回答的。也就是只有这个问题之前的信息才为已知信息。

老板说,只有回答对他全部的问题,伊卡洛斯才能吃到瓜,他聪明的想到了让你来帮助他。

Input

第一行包含两个整数N

M N是西瓜总数, M是以 A Q开头的老板的话总和。
以下 M行,每行包含一条老板的话。形式有 A x y 1 A x y 2 Q x y 1N100000 1M200000 1X,YN

数据保证没有矛盾

Output

对于每一条Q

指令,输出1/2/3代表两个西瓜颜色的关系。

Sample input and output

Sample Input Sample Output
6 9  
A 1 2 1  
A 1 3 1  
A 1 4 2  
Q 2 4  
Q 1 6  
A 3 6 1  
A 4 5 2  
Q 1 5  
Q 1 6
2
3
3  
1

Hint

西瓜的颜色可以有无数多种!

上面一题的加强版,不能单独去对立了,因为这里不是单单只有两种颜色,如果说x和y,y和z不同颜色,那么我们只能说明他们不同颜色,不能直接证明x和z的关系。这里用到启发式合并(小的合并到大的),用stl的set表示和x不同的颜色的元素的集合,如果x和y是相同颜色,那么和x不同的颜色的,与和y不同的颜色的可以合并到一起,小的合并到大的,并且合并father并查集Union(x,y);如果x和y不同,那么set[x].insert(Find(y)),set[y].insert( Find(x)),直接找他们相同的根节点放到相对不同颜色的集合,这里就表示他们不同颜色了。查询的时候,如果Find(x)==Find(y)那么就是相同的颜色,如果不等,接着判断x的集合里面是否含有y||y的集合里面是否含有x,如果有就可以判断他们不同了,如果没有,那么就是不明情况
#include <iostream>
#include <cstdio>
#include <string>
#include <set>

using namespace std;
int father[100009];
set<int>s[100009];//代表s[x]代表和x不同的元素的集合
int Find(int x)
{
    if(x!=father[x]) father[x]=Find(father[x]);
    return father[x];
}
void Union(int i,int j)
{
    int x=Find(i);
    int y=Find(j);
    if(x!=y)
    {
        if(s[x].size()>s[y].size()) swap(s[x],s[y]);//小的合并到大的
        father[x]=y;
        for(set<int>::iterator it=s[x].begin(); it!=s[x].end(); it++)
        {
            s[y].insert(Find(*it));//找到根节点
        }
    }
}
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1; i<=n; i++)
    {
        father[i]=i;
    }
    while(m--)
    {
        char c[10];
        scanf("%s",c);
        if(c[0]=='A')
        {
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            if(z==1)
            {
                Union(x,y);
            }
            else
            {
                x=Find(x);
                y=Find(y);
                s[x].insert(y);
                s[y].insert(x);
            }
        }
        else
        {
            int x,y;
            scanf("%d%d",&x,&y);
            x=Find(x);
            y=Find(y);
            if(x==y) printf("1\n");
            else if(s[x].count(y)==1||s[y].count(x)==1) printf("2\n");//在对应的set里面的找到了相应元素
            //证明这两个元素是不同的
            else printf("3\n");
        }
    }
}

E - 小埋的steam愿望单

Time Limit: 2000 MS     Memory Limit: 64 MB

小埋有一个steam愿望单,上面记载着她想买的游戏!现在小埋有以下 $n$ 个操作:

$1\ x\ y$ 添加一个价格为 $y$ 名字为 $x$ 的游戏加入愿望单

$2\ x$ 删除名字为 $x$ 的游戏

$3\ x\ y$ 名字为 $x$ 的游戏价格调整为 $y$

$4\ x$ $x$ 为 $1$ 输出最便宜的游戏的名字(如果有多个同价格游戏输出字典序最小的),$x$ 为 $2$ 输出最贵的游戏(如果有多个同价格游戏输出字典序最大的)

不合法的情况请忽略该操作

不合法的情况请忽略该操作

不合法的情况请忽略该操作

Input

第一行一个$n(1\le n \le 1e5)$接下来 $n$ 行 每行一个 $p$ 表示操作类型 接下来根据操作类型跟一个或两个数字( $x$ 只包括大小写字母和下划线并不超过25个字符,$1\le y\le 1e9$)

Output

对于每个查询,输出一行对应游戏的名字

Sample input and output

Sample Input Sample Output
8
1 slay_the_spire 60
1 dark_soul_III 118
1 The_Binding_of_Isaac 58
1 Age_of_Empires_II 88
4 1
3 dark_soul_III 57
4 1
4 2
The_Binding_of_Isaac
dark_soul_III
Age_of_Empires_II
7
4 1
1 I 100
1 II 150
1 I 200
4 1
2 III
3 IV 600
set和map的应用,map中erase(对应值)就可以直接删除了,遍历的时候要m.erase(it++)(题外话),map<string,int>string代表名字,数字代表节点所在的位置。操作1,先找map是否已经存在这个元素,如果没有,把这个节点加入数组, 并且加入map,set并在map中记录在当前数组的位置,操作2,删除,map,set中删除这个节点,操作3,修改直接从map中读到这个节点的位置,然后在set中删除,然后再数组中修改,然后再加入set,操作4,重载,价格从小到大,相同时按字典序排行,那么set中就是价格从小到大,同时相同时字典序也是从小到大,所以第一个一定时最便宜字典序最小的,最后一个一定是最贵字典序最大的。注意判断set空的情况
#include<iostream>
#include<string>
#include<vector>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<set>
#include<stack>
#include<queue>
#include<string>
#include<map>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
struct node
{
    int v;
    string name;
    friend bool operator < (node a,node b)
    {
        if(a.v==b.v) return a.name<b.name;
        else return a.v<b.v;
    }
} c[100009];
set<node>s;
map<string,int>m;
int main()
{
    int n,ans=1;
    scanf("%d",&n);
    while(n--)
    {
        int flag,v;
        string a;
        node temp;
        scanf("%d",&flag);
        if(flag==1)
        {
            cin>>a>>v;
            if(m.count(a)==0)
            {
                temp.name=a;
                temp.v=v;
                s.insert(temp);
                m[a]=ans;
                c[ans++]=temp;
            }
        }
        else if(flag==2)
        {
            cin>>a;
            if(m.count(a)==1)
            {
                s.erase(c[m[a]]);
                m.erase(a);
            }
        }
        else if(flag==3)
        {
            cin>>a>>v;
            if(m.count(a)==1)
            {
                s.erase(c[m[a]]);
                c[m[a]].v=v;
                s.insert(c[m[a]]);
            }
        }
        else
        {
            set<node>::iterator it;
            cin>>v;
            if(s.size())
            {
                if(v==1)
                {
                    it=s.begin();
                    cout<<(*it).name<<endl;
                }
                else
                {
                    it=s.end();
                    it--;
                    cout<<(*it).name<<endl;
                }
            }
        }
    }
  
}

三澄美琴是一个热爱学习的法医,今天晚上她准备下载很多很多法医论文资料。下面有三个操作

1 ti ai bi

i ti时刻加入了编号为 ai耗时为 bi

的论文进入下载队列

2 ti

ti

时刻取消队列首位的任务(如果下载队列为空就忽略该操作)

3 ti

查询在 ti

时刻队列首位的任务编号,无下载任务输出 -1

Input

第一行一个n

(1n1e5)
接下来 n行,每行 j(1j3),ti(1ti1e9,),当 j=1 时, ti 后会跟 ai(1ain), bi(1bi1e4)

Output

对于每个3号操作输出一行,输出目前队列首位的论文编号

Sample input and output

Sample Input Sample Output
6
1 1 1 5
3 2
1 3 2 3
2 4
3 5
3 6
1
2
-1

Hint

样例解释

操作一:在第1秒加入了编号为1的论文,耗时5秒,将于第6秒完成

操作二:在第1秒查询,队列首位为编号1

操作三:在第3秒加入编号为2的论文下载,耗时3秒,将于第6秒完成,队列里有两个下载任务,队列首位为编号1

操作四:在第4秒移除了编号为1的论文,目前队列里有一个下载任务,队列首位为编号2

操作五:在第5秒查询,队列首位为编号2

操作六:在第6秒查询,此时2号任务刚好完成,队列为空,输出-1

单纯队列,注意一下就好了

#include<iostream>
#include<string>
#include<vector>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<set>
#include<stack>
#include<queue>
#include<string>
#include<map>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
struct node
{
    int id,t;
};
queue<node>q;
int main()
{
    int m;
    scanf("%d",&m);
    while(m--)
    {
        int op,begin,t;
        node temp;
        scanf("%d",&op);
        if(op==1)
        {

           cin>>begin>>temp.id>>t;
           temp.t=begin+t;
           q.push(temp);
        }
        else if(op==2)
        {
            cin>>t;
            while(!q.empty())
            {
                if(t>=q.front().t) q.pop();
                else
                {
                    q.pop();
                    break;
                }
            }
        }
        else
        {
            cin>>t;
            while(!q.empty())
            {
                if(t>=q.front().t) q.pop();
                else
                {
                    printf("%d\n",q.front().id);
                    break;
                }
            }
            if(q.empty()) printf("-1\n");
        }
    }
}



猜你喜欢

转载自blog.csdn.net/keepcoral/article/details/80671376
今日推荐