• 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)判断一次。

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

  • 相关阅读:
    C语言 unsigned 和 signed 类型相互转换深入理解
    C++的关键字 explicit的作用———菜鸟级日记
    solr笔记
    矩阵解
    USACO题目——Transformations
    模块化加载时断点调试没反应,进入不了断点
    flash中的渐变滤镜GradientGlowFilter
    Flash Builder 找不到所需的Adobe Flash Player调试器版本的解决办法
    使用Pixel Bender 和Shader Job来处理普通数据运算
    关于Event.ADDED_TO_STAGE
  • 原文地址:https://www.cnblogs.com/mrclr/p/15064311.html
Copyright © 2020-2023  润新知