您的位置:首页 > 其它

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

2017-07-14 22:32 363 查看

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
;
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
;
int n, m;
bool rep
;
double x
, y
;
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
;
int a
[5], n, m, tot
;
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
;
int s
, t
, 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
, b
;
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
, dp
, par
;
bool f
;
vector<int> a
;
vector<int> tree
;
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
, dis
, tle[100];
int n, s, t;
bool f
;
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
, a
;
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,  难过
希望自己在暑期集训收货更大吧
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  并查集 图论 搜索