Codeforces Round #720 (Div. 2) 题解

https://codeforces.com/contest/1521

C起码该出的,结果比赛时凌乱了,我是fw。
参考洛绫璃

A题

题意:
给你两个数A,B,定义一个数满足\(x mod (A * B) = 0\)是好的数,否则只满足\(x mod A = 0\)是近似好的数,
让你构造三个各不相同的数x,y,z满足,只有一个是好的数,其它两个是近似好的数,且\(x + y = z\)

思路:
注意到 \(gcd(x, x + 1) = 0\), 构造\(a + (b - 1) = a * b\)即可
注意b = 2时,前两项相等,因此把b扩大一倍即可。

ll a, b;
 
int main()
{
	IOS; 
	int T;
	cin >> T;
	while(T --)
	{
		cin >> a >> b;
		if(b == 1) cout << "NO\n";
		else
		{
			cout << "YES\n";
			b = b * 2;
			cout << a << " " << (b - 1) * a << " " << a * b << endl; 
		}
	 } 
	return 0;
}

B题

题意:
给你一个数组a,允许你进行操作选定两个下标i, j,
满足\(min(a_i, a_j) = min(x, y)\),即可将对应的数变成x,y。

思路:
注意到 \(gcd(x, x + 1) = 0\)
因此找到最小的数,然后往左右两边不断 + 1构造即可。

ll n;
ll a[N];
struct node{
	int i, j, x, y;
};
vector<node> v;
 
int main()
{
	IOS; 
	int T;
	cin >> T;
	while(T --)
	{
		v.clear();
		cin >> n;
		int mind = 1e9 + 10, d;
		for(int i = 1 ; i <= n ; i ++)
		{
			cin >> a[i];
			if(mind > a[i])
			{
				mind = a[i];
				d = i;
			}
		}
		
		int x = mind;
		for(int i = d + 1 ; i <= n ; i ++)
			v.push_back({d, i, mind, ++ x});
		x = mind;
		for(int i = d - 1 ; i >= 1 ; i --)
			v.push_back({i, d, ++ x, mind});
 
		cout << v.size() << "\n";
		for(auto x : v)
			cout << x.i << " " << x.j << " " << x.x << " " << x.y << "\n";
	 } 
 
	return 0;
} 

C题

题意:
交互题,支持下图询问,然你猜出整个数组(该数组是一个1到n的排列)

思路:
\(n / 2\)次找到1,然后用n次确定所有其他数,因此花费\((n * 3) / 2\)

int a[N];
int n;
int ask(int x, int y, int z, bool f)
{
    if (!f) cout << "? 1 " << x << ' ' << y << ' ' << z << endl;
    else cout << "? 2 " << x << ' ' << y << ' ' << z << endl;
    cin >> x; return x;
 } 
 
int main()
{
	IOS; 
	int T;
	cin >> T;
	while(T --)
	{
		cin >> n;
		int id;
		for(int i = 1 ; i <= n ; i += 2)
		{
			if(i + 1 <= n)
			{
				int c = ask(i, i + 1, 1, 1);
				if(c == 1)
				{
					a[i] = 1;
					id = i;
					break;
				}
				else if(c == 2 && ask(i + 1, i, 1, 1) == 1)
				{
					a[i + 1] = 1;
					id = i + 1;
					break;
				}
			}
			else
			{
				id = i;
				a[i] = 1;
				break;
			}
		}
		
		for(int i = 1 ; i <= n ; i ++)
			if(id ^ i) a[i] = ask(id, i, n - 1, 0);
		cout << "! ";
		for(int i = 1 ; i <= n ; i ++) cout << a[i] << " ";
		cout << endl;
	 } 
 
	return 0;
} 

D题

题意:
给你一棵树,每次可以删一条边然后加一条边,问你最少操作多少次可以把树变成一条链
输出操作方案

思路:
dfs遍历, 然后根据儿子数目情况操作,详情见如下代码

int n;
vector<pii> v1, v2;
 
int h[N], e[M], ne[M], idx;
 
void add(int a, int b)
{
	e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}
 
int dfs(int u, int fa)
{
	int l = 0, r = 0;
	for(int i = h[u] ; ~i ; i = ne[i])
	{
		int j = e[i];
		if(j == fa) continue;
		int cur = dfs(j, u);	//当前儿子所处的子树是否还有能连边的节点 
		if(!cur) continue;
		//如果已经有两个儿子,就直接把边切了 
		if(r) v1.push_back({u, j}), v2.push_back({j, cur});
		else if(!l) l = cur;
		else if(!r) r = cur;
	}
	
	//如果有父亲,两个儿子,就把和父亲的边切了,然后把两个儿子放进v2 
	if(l && r && fa)
		v1.push_back({fa, u}), v2.push_back({l, r});
		
	//	return l ? (r && fa) ? 0 : l : u;
	if(l)
	{
		if(r && fa) return 0;	//和父亲的边被切掉了 
		else return l;	//有左儿子返回左儿子 
	}
	return u;  //当前结点没有左右儿子了 
}
 
int main()
{
	IOS; 
	int T;
	cin >> T;
	while(T --)
	{
		cin >> n;
		idx = 0;
		for(int i = 1 ; i <= n ; i ++) h[i] = -1;
		for(int i = 1 ; i < n ; i ++)
		{
			int a, b;
			cin >> a >> b;
			add(a, b), add(b, a);
		}
			
		int m = dfs(1, 0);
		cout << v1.size() << "\n";
		for(int i = 0 ; i < v1.size() ; ++ i)
			cout << v1[i].x << " " << v1[i].y << " " << m << " " << v2[i].x << "\n", m = v2[i].y;
		v1.clear(), v2.clear();
	 } 
 
	return 0;
} 
 
/*
1
7
1 2
1 3
1 4
1 5
5 6
5 7
*/
posted @ 2021-05-10 16:08  beatlesss  阅读(88)  评论(1编辑  收藏  举报