Codeforces 847 Div3 题解A-G

Codeforces 847 Div3 题解A-G

好久不打了,这几周忙着写各种作业,然后发现代码力跟不上了。今天复健一下吧。反正也是比较碎片的时间,马上新的作业就会被布置下来,n久没打了就来看下。所以这次先捡div3复键一下,之后暑假等实习了到时候得认真上上div2,不然秋招g咯。

A. Polycarp and the Day of Pi

这个题题意给出一个1-30位string,问猜圆周率的人猜对了几位

这个本来想用acos(-1)按照位进行比对,后来发现题目中给了圆周率的前30位,那这题就是白送了。

AC 代码
#include <bits/stdc++.h>
using namespace std;
constexpr int limit =  (4e5  + 5);//防止溢出
#define INF 0x3f3f3f3f
#define inf 0x3f3f3f3f3f3f3f
#define lowbit(i) i&(-i)//一步两步
#define EPS 1e-9
#define FASTIO  ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);
#define pi(a, b) pair<a,b>
#define rep(i, a, b) for(ll i = a; i <= b ; ++i)
#define per(i, a, b) for(ll i = b ; i >= a  ; --i)
#define MOD 998244353
#define traverse(u) for(int i = head[u]; ~i ; i = edge[i].next)
#define FOPEN freopen("C:\\Users\\tiany\\CLionProjects\\akioi\\data.txt", "rt", stdin)
#define FOUT freopen("C:\\Users\\tiany\\CLionProjects\\akioi\\dabiao.txt", "wt", stdout)
typedef long long ll;
typedef unsigned long long ull;
char buf[1 << 23], *p1 = buf, *p2 = buf, obuf[1 << 23], *O = obuf;
inline ll read() {
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
    ll sign = 1, x = 0;
    char s = getchar();
    while (s > '9' || s < '0') {
        if (s == '-')sign = -1;
        s = getchar();
    }
    while (s >= '0' && s <= '9') {
        x = (x << 3) + (x << 1) + s - '0';
        s = getchar();
    }
    return x * sign;
#undef getchar
}//快读
void print(ll x) {
    if (x / 10) print(x / 10);
    *O++ = x % 10 + '0';
}

void write(ll x, char c = 't') {
    if (x < 0)putchar('-'), x = -x;
    print(x);
    if (!isalpha(c))*O++ = c;
    fwrite(obuf, O - obuf, 1, stdout);
    O = obuf;
}


int n,m;
int a[limit];
void solve(){
	string str;
	cin>>str;
	n = str.length();
	str = " " + str;
	string num = " 314159265358979323846264338327";
	rep(i,1,n){
		int x = str[i] - '0';
		if(str[i] != num[i]){
			cout<<i-1<<endl;
			return;
		}
	}
	cout<<n<<endl;
};
int32_t main() {
#ifdef LOCAL
    FOPEN;
//    FOUT;
#endif
    FASTIO
    int kase;
    cin>>kase;
    while (kase--)
        invoke(solve);
    cerr << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC << "s";
    return 0;
}

B. Taisia and Dice

这个题题意是给出一个n个骰子的序列,然后我们知道筛子上面的总和是s,然后去掉最大的那个总和是r,要求给出一种序列构造。

这个题就是贪心,懒得讲了,直接看代码。

AC 代码
#include <bits/stdc++.h>
using namespace std;
constexpr int limit =  (4e5  + 5);//防止溢出
#define INF 0x3f3f3f3f
#define inf 0x3f3f3f3f3f3f3f
#define lowbit(i) i&(-i)//一步两步
#define EPS 1e-9
#define FASTIO  ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);
#define pi(a, b) pair<a,b>
#define rep(i, a, b) for(ll i = a; i <= b ; ++i)
#define per(i, a, b) for(ll i = b ; i >= a  ; --i)
#define MOD 998244353
#define traverse(u) for(int i = head[u]; ~i ; i = edge[i].next)
#define FOPEN freopen("C:\\Users\\tiany\\CLionProjects\\akioi\\data.txt", "rt", stdin)
#define FOUT freopen("C:\\Users\\tiany\\CLionProjects\\akioi\\dabiao.txt", "wt", stdout)
typedef long long ll;
typedef unsigned long long ull;
char buf[1 << 23], *p1 = buf, *p2 = buf, obuf[1 << 23], *O = obuf;
inline ll read() {
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
    ll sign = 1, x = 0;
    char s = getchar();
    while (s > '9' || s < '0') {
        if (s == '-')sign = -1;
        s = getchar();
    }
    while (s >= '0' && s <= '9') {
        x = (x << 3) + (x << 1) + s - '0';
        s = getchar();
    }
    return x * sign;
#undef getchar
}//快读
void print(ll x) {
    if (x / 10) print(x / 10);
    *O++ = x % 10 + '0';
}

void write(ll x, char c = 't') {
    if (x < 0)putchar('-'), x = -x;
    print(x);
    if (!isalpha(c))*O++ = c;
    fwrite(obuf, O - obuf, 1, stdout);
    O = obuf;
}


int n,m;
int a[limit];
void solve(){
	int s,r;
	cin>>n>>s>>r;
	a[1] = s - r;//现在的
	int rem = s - (n - 1) - a[1];
	rep(i,2,n){
		int minn = max(0, min({rem,  a[1] - 1}));
		a[i] = 1 + minn;
		rem -= minn;
	}
	per(i,1,n){
		cout<<a[i]<<" ";
	}
	cout<<endl;
};
int32_t main() {
#ifdef LOCAL
    FOPEN;
//    FOUT;
#endif
    FASTIO
    int kase;
    cin>>kase;
    while (kase--)
        invoke(solve);
    cerr << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC << "s";
    return 0;
}
    

C. Premutation

这题题意是,我们有个permutation,然后我们操作n次,第i次遮住第i个数字,然后所有操作序列给出,但是未必是按照顺序来的,要求我们复原这个permutation。

这个题看样例,我们发现第一列必然只有一个数字出现了1次,而且每一列有且只有两个数字,那么我们想一下就会发现两件事

  • 第一列只出现一次的数字是permutation的第二个数字, 出现n-1次的数字是permutation的第一个数字
  • 我们可以通过一列数字来推出当前和接下来一位总共两位数字

这个性质是有传递性的,也就是说我们可以通过前两列来推出第三列,第三列来推出第四列,以此类推。

接下来就很简单了,我手太生了,写锅了好几次,不过最后还是写出来了。

AC 代码
#include <bits/stdc++.h>
using namespace std;
constexpr int limit =  (4e5  + 5);//防止溢出
#define INF 0x3f3f3f3f
#define inf 0x3f3f3f3f3f3f3f
#define lowbit(i) i&(-i)//一步两步
#define EPS 1e-9
#define FASTIO  ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);
#define pi(a, b) pair<a,b>
#define rep(i, a, b) for(ll i = a; i <= b ; ++i)
#define per(i, a, b) for(ll i = b ; i >= a  ; --i)
#define MOD 998244353
#define traverse(u) for(int i = head[u]; ~i ; i = edge[i].next)
#define FOPEN freopen("C:\\Users\\tiany\\CLionProjects\\akioi\\data.txt", "rt", stdin)
#define FOUT freopen("C:\\Users\\tiany\\CLionProjects\\akioi\\dabiao.txt", "wt", stdout)
typedef long long ll;
typedef unsigned long long ull;
char buf[1 << 23], *p1 = buf, *p2 = buf, obuf[1 << 23], *O = obuf;
inline ll read() {
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
    ll sign = 1, x = 0;
    char s = getchar();
    while (s > '9' || s < '0') {
        if (s == '-')sign = -1;
        s = getchar();
    }
    while (s >= '0' && s <= '9') {
        x = (x << 3) + (x << 1) + s - '0';
        s = getchar();
    }
    return x * sign;
#undef getchar
}//快读
void print(ll x) {
    if (x / 10) print(x / 10);
    *O++ = x % 10 + '0';
}

void write(ll x, char c = 't') {
    if (x < 0)putchar('-'), x = -x;
    print(x);
    if (!isalpha(c))*O++ = c;
    fwrite(obuf, O - obuf, 1, stdout);
    O = obuf;
}


int n,m;
int a[limit];
int mp[105][105];
void solve(){
	cin>>n;
	rep(i,1,n){
		rep(j, 1, n - 1){
			cin>>mp[i][j];
		}
	}
	set<int>s;
	rep(i,1,n){
		s.insert(i);
	}
	map<int, int>mpp;
	rep(i,1,n){
		mpp[mp[i][1]]++;
	}
	int now = -1;
	int last;
	for(auto [k, v]: mpp){
//		cout<<k<<" "<<v<<endl;
		if(v == 1){
			now = k;
		}else{
			last = k;
		}
	}
	a[1] = last;
	s.extract(last);
	int tot = 2;
	int next = now;
	rep(j,2,n - 1){
		int res = 0;
		rep(i,1,n){
			if(mp[i][j] != next){ //如果现在还不是下一个
				res = mp[i][j];

			}else{
				a[tot] = mp[i][j];
			}
		}
		s.extract(a[tot++]);
		next = res;
	}
	if(s.size()){
		a[n] = *s.begin();
	}
	rep(i,1,n){
		cout<<a[i]<<" ";
	}
	cout<<endl;
};
int32_t main() {
#ifdef LOCAL
    FOPEN;
//    FOUT;
#endif
    FASTIO
    int kase;
    cin>>kase;
    while (kase--)
        invoke(solve);
    cerr << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC << "s";
    return 0;
}

D. Matryoshkas

这个题问你,给出n个数字,要求我们拆解成尽可能少的公差为1的等差数列,第一项随意,然后输出最少能拆成几个。

这个题首先暴力是没法划分的(显然)。我甚至想到了网络流,刚准备建图,但总觉得有更简单的做法。考虑一下之后,发现我们可以从出现次数最少的入手,那么我们每次连续删除以当前出现次数最少的数字为中心(其实这个说法不准确,应该是包含这个数字的连续序列),贪心地向两边扩展,直到遇到第一个没出现的数字为止。我们用map记录下数字的出现次数。最后操作总数一定是bound在O(N)的,复杂度也就是O(NlogN)了。

AC 代码
#include <bits/stdc++.h>
using namespace std;
constexpr int limit =  (4e5  + 5);//防止溢出
#define INF 0x3f3f3f3f
#define inf 0x3f3f3f3f3f3f3f
#define lowbit(i) i&(-i)//一步两步
#define EPS 1e-9
#define FASTIO  ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);
#define pi(a, b) pair<a,b>
#define rep(i, a, b) for(ll i = a; i <= b ; ++i)
#define per(i, a, b) for(ll i = b ; i >= a  ; --i)
#define MOD 998244353
#define traverse(u) for(int i = head[u]; ~i ; i = edge[i].next)
#define FOPEN freopen("C:\\Users\\tiany\\CLionProjects\\akioi\\data.txt", "rt", stdin)
#define FOUT freopen("C:\\Users\\tiany\\CLionProjects\\akioi\\dabiao.txt", "wt", stdout)
typedef long long ll;
typedef unsigned long long ull;
char buf[1 << 23], *p1 = buf, *p2 = buf, obuf[1 << 23], *O = obuf;
inline ll read() {
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
    ll sign = 1, x = 0;
    char s = getchar();
    while (s > '9' || s < '0') {
        if (s == '-')sign = -1;
        s = getchar();
    }
    while (s >= '0' && s <= '9') {
        x = (x << 3) + (x << 1) + s - '0';
        s = getchar();
    }
    return x * sign;
#undef getchar
}//快读
void print(ll x) {
    if (x / 10) print(x / 10);
    *O++ = x % 10 + '0';
}

void write(ll x, char c = 't') {
    if (x < 0)putchar('-'), x = -x;
    print(x);
    if (!isalpha(c))*O++ = c;
    fwrite(obuf, O - obuf, 1, stdout);
    O = obuf;
}


int n,m;
int a[limit];
struct node{
	int val, cnt;
	bool operator < (const node &rhs) const{
		return val < rhs.val;
	}
};
void solve(){
	cin>>n;
	rep(i,1,n){
		cin>>a[i];
	}
	sort(a + 1, a + 1 + n);
	int ans = 0;
	map<int, int>mp;
	for_each(a + 1, a + 1 + n, [&](int x){mp[x]++;});
	vector<node>v;
	for(auto [k, vv] : mp){
		v.push_back({vv, k});
	}
	sort(v.begin(), v.end());
	for(auto [cnt, num] : v){
		rep(_, 1, cnt){
			if(!mp[num])continue;
			per(i,1,num){
				if(!mp[i]){
					break;
				}
				mp[i]--;
			}
			rep(i, num + 1, 1e12){
				if(!mp[i]){
					break;
				}
				mp[i]--;
			}
			++ans;
		}
	}
	cout<<ans<<endl;

};
int32_t main() {
#ifdef LOCAL
    FOPEN;
//    FOUT;
#endif
    FASTIO
    int kase;
    cin>>kase;
    while (kase--)
        invoke(solve);
    cerr << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC << "s";
    return 0;
}

E. Vlad and a Pair of Numbers

这个题是给出来a + b的和,要求找出来a和b使得

a \oplus b = \frac{a + b}{2}

如果不存在就输出1.

首先我们反向思维一下,这题的本质是a + b异或,然后右移一位。

首先我们可以确定,因为右移的关系,替换到左边就是左移,所以这个\(a \oplus b\)的值一定是偶数。

然后我们考虑一下i和i - 1位置上之间的关系,如果\(a \oplus b\)的值在第i位是1,那么只能说明一个问题,就是当前位置上是1和0(分别),那么如果这一位是这样,前一位的就不能是1,因为这样就会导致异或的结果是奇数,所以我们可以得到二个结论,就是如果\(a \oplus b\)的值在第i位是1,那么第i - 1位a和b一定都是1,否则答案就不存在。

然后上代码

AC 代码
#include <bits/stdc++.h>
using namespace std;
constexpr int limit =  (4e5  + 5);//防止溢出
#define INF 0x3f3f3f3f
#define inf 0x3f3f3f3f3f3f3f
#define lowbit(i) i&(-i)//一步两步
#define EPS 1e-9
#define FASTIO  ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);
#define pi(a, b) pair<a,b>
#define rep(i, a, b) for(ll i = a; i <= b ; ++i)
#define per(i, a, b) for(ll i = b ; i >= a  ; --i)
#define MOD 998244353
#define traverse(u) for(int i = head[u]; ~i ; i = edge[i].next)
#define FOPEN freopen("C:\\Users\\tiany\\CLionProjects\\akioi\\data.txt", "rt", stdin)
#define FOUT freopen("C:\\Users\\tiany\\CLionProjects\\akioi\\dabiao.txt", "wt", stdout)
typedef long long ll;
typedef unsigned long long ull;
char buf[1 << 23], *p1 = buf, *p2 = buf, obuf[1 << 23], *O = obuf;
inline ll read() {
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
    ll sign = 1, x = 0;
    char s = getchar();
    while (s > '9' || s < '0') {
        if (s == '-')sign = -1;
        s = getchar();
    }
    while (s >= '0' && s <= '9') {
        x = (x << 3) + (x << 1) + s - '0';
        s = getchar();
    }
    return x * sign;
#undef getchar
}//快读
void print(ll x) {
    if (x / 10) print(x / 10);
    *O++ = x % 10 + '0';
}

void write(ll x, char c = 't') {
    if (x < 0)putchar('-'), x = -x;
    print(x);
    if (!isalpha(c))*O++ = c;
    fwrite(obuf, O - obuf, 1, stdout);
    O = obuf;
}


int n,m;
int a[limit];
struct node{
	int val, cnt;
	bool operator < (const node &rhs) const{
		return val < rhs.val;
	}
};
void solve(){
	cin>>n;
	rep(i,1,n){
		cin>>a[i];
	}
	sort(a + 1, a + 1 + n);
	int ans = 0;
	map<int, int>mp;
	for_each(a + 1, a + 1 + n, [&](int x){mp[x]++;});
	vector<node>v;
	for(auto [k, vv] : mp){
		v.push_back({vv, k});
	}
	sort(v.begin(), v.end());
	for(auto [cnt, num] : v){
		rep(_, 1, cnt){
			if(!mp[num])continue;
			per(i,1,num){
				if(!mp[i]){
					break;
				}
				mp[i]--;
			}
			rep(i, num + 1, 1e12){
				if(!mp[i]){
					break;
				}
				mp[i]--;
			}
			++ans;
		}
	}
	cout<<ans<<endl;

};
int32_t main() {
#ifdef LOCAL
    FOPEN;
//    FOUT;
#endif
    FASTIO
    int kase;
    cin>>kase;
    while (kase--)
        invoke(solve);
    cerr << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC << "s";
    return 0;
}

F. Timofey and Black-White Tree

这题真不会,是vp结束之后看jiangly代码写的,这题本质是有根树,每次把节点涂黑,问黑节点当中距离最小的值是多少。

我之前想的是用lca,维护lca群,然后每次更新的时候分为两种情况,一种是lca在底下,那么记录一下lca下面离得最近的黑色节点,然后更新ans,另一种是lca在上面,那么就把lca的父亲节点的最近的黑色节点更新一下,然后更新ans,再把所有淘汰的节点从里面pop掉,重构lca群。

听上去很无敌,但是读了下题发现是有根树,那就不行了。

赛后看的是,应该是简单的bfs,用贪心去收集答案,在bfs的时候每次只有答案小于当前的最小值才更新,复杂度\(O(n\sqrt n)\)

然后上代码

AC 代码
#include <bits/stdc++.h>
using namespace std;
constexpr int limit =  (4e5  + 5);//防止溢出
#define INF 0x3f3f3f3f
#define inf 0x3f3f3f3f3f3f3f
#define lowbit(i) i&(-i)//一步两步
#define EPS 1e-9
#define FASTIO  ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);
#define pi(a, b) pair<a,b>
#define rep(i, a, b) for(ll i = a; i <= b ; ++i)
#define per(i, a, b) for(ll i = b ; i >= a  ; --i)
#define MOD 998244353
#define traverse(u) for(int i = head[u]; ~i ; i = edge[i].next)
#define FOPEN freopen("C:\\Users\\tiany\\CLionProjects\\akioi\\data.txt", "rt", stdin)
#define FOUT freopen("C:\\Users\\tiany\\CLionProjects\\akioi\\dabiao.txt", "wt", stdout)
typedef long long ll;
typedef unsigned long long ull;
char buf[1 << 23], *p1 = buf, *p2 = buf, obuf[1 << 23], *O = obuf;
inline ll read() {
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
    ll sign = 1, x = 0;
    char s = getchar();
    while (s > '9' || s < '0') {
        if (s == '-')sign = -1;
        s = getchar();
    }
    while (s >= '0' && s <= '9') {
        x = (x << 3) + (x << 1) + s - '0';
        s = getchar();
    }
    return x * sign;
#undef getchar
}//快读
void print(ll x) {
    if (x / 10) print(x / 10);
    *O++ = x % 10 + '0';
}

void write(ll x, char c = 't') {
    if (x < 0)putchar('-'), x = -x;
    print(x);
    if (!isalpha(c))*O++ = c;
    fwrite(obuf, O - obuf, 1, stdout);
    O = obuf;
}


int n,m;
int a[limit];
vector<int>g[limit];
int c[limit];
int fa[limit];
int find(int x){
	return x == fa[x] ? x : fa[x] = find(fa[x]);
}
void merge(int x,int y){
	int fx = find(x);
	int fy = find(y);
	if(fx != fy){
		fa[fx] = fy;
	}
}
int sizes[limit], pre[limit], son[limit], dep[limit], top[limit], dfn[limit], tot;
void dfs1(int u, int f, int d) {
	dep[u] = d;
	sizes[u] = 1;
	pre[u] = f;
	for (auto v : g[u]) {
		if (v == f)continue;
		dfs1(v, u, d + 1);
		sizes[u] += sizes[v];
		if (sizes[v] > sizes[son[u]])son[u] = v;
	}
}
void dfs2(int u, int t) {
	dfn[u] = ++tot;
	top[u] = t;
	if (son[u])dfs2(son[u], t);
	for (auto v : g[u]) {
		if (v == pre[u] || v == son[u])continue;
		dfs2(v, v);
	}
}
int lca(int x, int y) {
	while (top[x] != top[y]) {
		if (dep[top[x]] < dep[top[y]])swap(x, y);
		x = pre[top[x]];
	}
	return dep[x] < dep[y] ? x : y;
}
int dis(int x, int y) {
	return dep[x] + dep[y] - 2 * dep[lca(x, y)];
}
int dist[limit];
void solve(){
	cin>>n>>c[1];
	rep(i,1,n){
		g[i].clear();
		fa[i] = i;\
		dist[i] = numeric_limits<int>::max();
	}
	rep(i,2,n){
		cin>>c[i];
	}
	rep(i,1,n - 1){
		int u,v;
		cin>>u>>v;
		g[u].push_back(v);
		g[v].push_back(u);
	}
	int ans = INF;
	auto bfs = [&](int x){
		queue<int>q;
		q.push(x);
		dist[x] = 0;
		while(!q.empty()){
			int u = q.front();
			q.pop();
			for(auto v : g[u]){
				if(dist[v] > dist[u] + 1 and dist[u] + 1 < ans ){
					dist[v] = dist[u] + 1;
					q.push(v);
				}
			}
		}
	};
	bfs(c[1]);
	rep(i,2,n){
		a[i] = ans = min(ans,dist[c[i]]);
		bfs(c[i]);
	}
	rep(i,2,n){
		cout<<a[i]<<' ';
	}
	cout<<endl;
};
int32_t main() {
#ifdef LOCAL
    FOPEN;
//    FOUT;
#endif
    FASTIO
    int kase;
    cin>>kase;
    while (kase--)
        invoke(solve);
    cerr << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC << "s";
    return 0;
}

G. Tokens on Graph

这个题是给了一些token和一个无向图,我们的任务是把一个token移动到编号为1的节点上去。然后有一些bonus节点,没有使用次数和流量限制,但是我们不能连续移动一个token,而且我们每次需要把节点上的token移动到bonus节点上去才能继续走下一次。问给出一个构造图,能不能在一轮游戏中把一个token移动到编号为1的节点上去。

我卡在F了一会儿,发现关注的人都去做G了,所以我也来看G。

这道题我们发现,首先需要有至少一个token,其通往节点1的路径最短,且途中必须全部由bonus节点组成。我们有若干个token,把这个token拿掉,继续看其他token,所以每个token可以移动1次或者无数次(因为没有使用次数限制,如果路上有两个bonus节点就back and forth就好了),如果不存在无数次的,那么我们看看选中最短路的费用 - 1是否可以由其他只能move一次的token抵消掉.如果可以,那么yes,否则no

然后wa了,发现我们在统计步长为1的链的时候,对答案贡献最多为一次,改了就过了

AC代码
#include <bits/stdc++.h>
using namespace std;
constexpr int limit =  (4e5  + 5);//防止溢出
#define INF 0x3f3f3f3f
#define inf 0x3f3f3f3f3f3f3f
#define lowbit(i) i&(-i)//一步两步
#define EPS 1e-9
#define FASTIO  ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);
#define pi(a, b) pair<a,b>
#define rep(i, a, b) for(ll i = a; i <= b ; ++i)
#define per(i, a, b) for(ll i = b ; i >= a  ; --i)
#define MOD 998244353
#define traverse(u) for(int i = head[u]; ~i ; i = edge[i].next)
#define FOPEN freopen("C:\\Users\\tiany\\CLionProjects\\akioi\\data.txt", "rt", stdin)
#define FOUT freopen("C:\\Users\\tiany\\CLionProjects\\akioi\\dabiao.txt", "wt", stdout)
typedef long long ll;
typedef unsigned long long ull;
char buf[1 << 23], *p1 = buf, *p2 = buf, obuf[1 << 23], *O = obuf;
inline ll read() {
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
    ll sign = 1, x = 0;
    char s = getchar();
    while (s > '9' || s < '0') {
        if (s == '-')sign = -1;
        s = getchar();
    }
    while (s >= '0' && s <= '9') {
        x = (x << 3) + (x << 1) + s - '0';
        s = getchar();
    }
    return x * sign;
#undef getchar
}//快读
void print(ll x) {
    if (x / 10) print(x / 10);
    *O++ = x % 10 + '0';
}

void write(ll x, char c = 't') {
    if (x < 0)putchar('-'), x = -x;
    print(x);
    if (!isalpha(c))*O++ = c;
    fwrite(obuf, O - obuf, 1, stdout);
    O = obuf;
}


int n,m;
int a[limit];
vector<int> g[limit], g2[limit];
int token[limit];
int bonus[limit];
int fa[limit];
int vis[limit];
int find(int x){
	return fa[x] == x ? x : fa[x] = find(fa[x]);
}
void merge(int x,int y){
	int fx = find(x);
	int fy = find(y);
	if(fx != fy){
		if(fx > fy)swap(fx,fy);
		fa[fx] = fy;
	}
}
void solve(){
	int p, b;
	cin>>n>>m;
	cin>>p>>b;
	rep(i,1,n){
		fa[i] = i;
	}
	rep(i,1,p){
		cin>>token[i];
	}
	rep(i,1,b){
		cin>>bonus[i];
	}
	sort(token + 1,token + 1 + p);
	sort(bonus + 1,bonus + 1 + b);
	rep(i,1,n){
		g[i].clear();
		vis[i] = 0;
	}
	rep(i,1,m){
		int u,v;
		cin>>u>>v;
		if(u != 1 and !binary_search(token + 1,token + 1 + p,u) and !binary_search(bonus + 1,bonus + 1 + b,u)){
			continue;
		}
		if(v != 1 and !binary_search(token + 1,token + 1 + p,v) and !binary_search(bonus + 1,bonus + 1 + b,v)){
			continue;
		}
		g[u].push_back(v);
		g[v].push_back(u);
	}
	if(binary_search(token + 1,token + 1 + p,1)){
		cout<<"Yes"<<endl;
		return;
	}
	auto [rt, cost] = [&]()->pi(int, int){
		queue<pi(int, int)>q;
		q.push({1, 0});
		vis[1] = 1;
		while(!q.empty()){
			auto [u, c] = q.front();
			q.pop();
			if(binary_search(token + 1,token + 1 + p,u)){
				return {u, c};
			}
			for(auto v : g[u]){
				if(!vis[v]){
					vis[v] = 1;
					q.push({v, c + 1});
				}
			}
		}
		return {0, 0};
	}();
//	cout<<rt<<" "<<cost<<endl;
	if(!rt){
		cout<<"No"<<endl;
		return;
	}

	int big = 0, small = 0;//分别代表反复横跳和直接跳到的次数
	rep(i,1,p){
		int res = 0;
		if(token[i] == rt)continue;
		queue<pi(int, int)>q;
		q.push({token[i], 0});
		while(q.size()){
			auto [u, c] = q.front();
			q.pop();
			if(c == 2){
				res--;
				big = 1;
				break;
			}
			if(c == 1){
				res++;
			}
			for(auto v : g[u]){
				if(binary_search(bonus + 1,bonus + 1 + b,v))
					q.push({v, c + 1});
			}
		}
		if(res > 0){
			small++;
		}
	}
//	cout<<big<<" "<<small<<endl;
	if(big){
		cout<<"Yes"<<endl;
		return;
	}
	if(small >= cost - 1){
		cout<<"Yes"<<endl;
		return;
	}
	cout<<"No"<<endl;
};
int32_t main() {
#ifdef LOCAL
    FOPEN;
//    FOUT;
#endif
    FASTIO
    int kase;
    cin>>kase;
    while (kase--)
        invoke(solve);
    cerr << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC << "s";
    return 0;
}

<\details>

posted @ 2023-04-09 04:20  tiany7  阅读(23)  评论(0编辑  收藏  举报