CF Editorial of Global Round 15部分题解

传送门


这一场是真刺激,B题卡了半天搞了个假算法,C,D猜的结论。结果B题fst了,C,D竟然都猜对了,然而三道题还是掉分了……

A Subsequence Permutation

水题。排序即可。



B Running for Gold

(t_i < t_j)表示(i)(j)优。这题关键在于,如果(t_i < t_j)(i)可能是夺金,而(j)必不可能夺金。

所以我们动态维护一个“可能夺金”的人(x),初始令(x=1),并依次和(2 sim n)比较:如果(t_x<t_i),保持不变,否则令(x=i)。因为在(i)之前有机会夺金的人只有(x),而现在(t_i<t_x),那么当前可能夺金的人就只有(i)了。

这样扫到(n),最后再循环一次判断(x)是否能真正夺金即可。时间复杂度(O(n)).

关键代码:

struct Node
{
	int r[6];
	In bool operator < (const Node& oth)const
	{
		int cnt = 0;
		for(int i = 1; i <= 5; ++i) cnt += r[i] < oth.r[i];
		return cnt >= 3;
	}
}t[maxn];

In int solve()
{
	int x = 1;
	for(int i = 1; i <= n; ++i)
		if(t[i] < t[x]) x = i;
	for(int i = 1; i <= n; ++i) 
		if(i != x && t[i] < t[x]) return -1;
	return x;
}



C Maximize the Intersections

这题官方题解比我讲的明白多了,而且代码更为简单,我只是阐明一个大题思路。

首先我结论是猜对了:对于剩下的(n-K)个“新弦”和空着的(2(n-K))个点,第(i)条弦的左端点连接第(i)个点,右端点连接第(i+n-K)个点,交点最多。

首先这样构造一定能保证剩下的(n-K)个点两两相交。

至于为什么和固定的“旧弦”的相交个数加起来也最多,按题解我是这么理解的:交换两条“新弦”不会改变这两条弦和其他弦相交的点的个数(自然就包括“旧弦”),因此新弦之间的连接不会对旧弦产生影响。

至于代码,我写的挺烂,题解的更好。

#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<queue>
#include<assert.h>
#include<ctime>
using namespace std;
#define enter puts("") 
#define space putchar(' ')
#define Mem(a, x) memset(a, x, sizeof(a))
#define In inline
#define forE(i, x, y) for(int i = head[x], y; ~i && (y = e[i].to); i = e[i].nxt)
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-8;
const int maxn = 1005;
In ll read()
{
	ll ans = 0;
	char ch = getchar(), las = ' ';
	while(!isdigit(ch)) las = ch, ch = getchar();
	while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
	if(las == '-') ans = -ans;
	return ans;
}
In void write(ll x)
{
	if(x < 0) x = -x, putchar('-');
	if(x >= 10) write(x / 10);
	putchar(x % 10 + '0');
}
int n, K, m;
struct Node
{
	int L, R;
}t[maxn];

int vis[maxn << 1];
int vis2[maxn];
In void calc(int x)
{
	int L = 1, R = 0, cnt = 0;
	while(vis[L]) ++L;
	for(int i = L + 1; i <= m; ++i)
		if(!vis[i])
		{
			++cnt;
			if(cnt == n - x + 1) {R = i; break;}
		}
	vis[L] = vis[R] = x;
	t[x] = (Node){L, R};
}

In int solve()
{
	for(int i = K + 1; i <= n; ++i) calc(i);
	int ret = 0;
	for(int i = 1; i <= n; ++i)
		for(int j = i + 1; j <= n; ++j)
			if((t[i].L < t[j].L && t[i].R < t[j].R && t[i].R > t[j].L) || (t[j].L < t[i].L && t[j].R < t[i].R && t[j].R > t[i].L))
				 ++ret;
	return ret;
}

int main()
{
	int T = read();
	while(T--)
	{
		n = read(), K = read();
		m = n * 2;
		fill(vis + 1, vis + m + 1, 0);
		fill(vis2 + 1, vis2 + n + 1, 0);
		for(int i = 1; i <= K; ++i)
		{
			int L = read(), R = read();
			if(L > R) swap(L, R);
			t[i] = (Node){L, R};
			vis[L] = vis[R] = i;
		}
		write(solve()), enter;
	}
	return 0;
}

题解的写法是先把(2(n-K))个空点按顺序找出来,那么可以直接(O(n))为弦分配点了,而不用想我每次现找。



D Array Differentiation

哈哈,这题的结论我竟然也猜出来了。

正确的思路是这样的:对于(a_i=b_j-b_k),想成图上的一条边(j o k),同时也就有(k o j)表示(-a_i)

那么(n)个点(n)条边,必然至少有一个环。而把这个环的边顺序依次加起来,就得到了(b_{t_1}-b_{t_2}+b_{t_2}-b_{t_3}+cdots+b_{t_{m-1}}-b_{t_m}+b_{t_m}-b_{t_1}=0),即(sumlimits_{i=1}^m s_ia_{t_i}=0,s_i in {-1, 1}).

而对于环外的点,我们根据前驱就能确定剩下的(b_k).

因此,存在合法序列(b)满足题意(Rightarrow) 存在集合(t),满足(sumlimits_{i=1}^m s_i a_{t_i}=0).

实际上,这是一个等价命题,下面证明他的必要性:
还是将条件(sumlimits_{i=1}^m s_i a_{t_i}=0)看成环,令(b_{t_1}=0),那么对于环内的(b_{t_i}),可以构造(b_{t_{i+1}}=b_{t_i}+s_ia_{t_i}),而对于环外的点,直接令(b_k=a_k)即可。至此就构造出了合法的序列(b).

证毕。

那么代码就十分好写了,直接(O(3^n))枚举(a_i)不选,还是选(a_i,-a_i)即可。

主要代码:

bool dfs(int now, int cnt, int sum)
{
	if(cnt && !sum) return 1;
	if(now > n) return 0;
	if(dfs(now + 1, cnt, sum)) return 1;
	if(dfs(now + 1, cnt + 1, sum + a[now])) return 1;
	if(dfs(now + 1, cnt + 1, sum  - a[now])) return 1;
	return 0;
}

int main()
{
	int T = read();
	while(T--)
	{
		n = read();
		for(int i = 1; i <= n; ++i) a[i] = read();
		puts(dfs(1, 0, 0) ? "YES" : "NO");
	}
	return 0;
}

然后比赛的时候我是怎么猜这个结论的呢,我令(a_i=b_1-b_{i+1}(i<n)),然后再(O(n^2))枚举(a_n)是否可行,但其实把(a_n=b_j-b_k)(b_j,b_k)(a_i)表示,会发现问题就变成了是否存在(x,y),满足(a_n=a_x-a_y).但这样显然是不对的,于是最后我就大胆推断,能否有(a_n=sumlimits_{i=1}^m s_ia_{t_i}(s_i in {-1, 1})).然后对于每一个(a_i)都作为(a_n)判断一次。

会发现,我这个式子移项后就和题解式子一样啦……

原文地址:https://www.cnblogs.com/mrclr/p/15064311.html