zoukankan      html  css  js  c++  java
  • CF1045B Space Isaac

    原题链接

    DOWNLOAD AS PDF

    题目大意

    (0sim m-1)的数被分成两个集合,你可以分别从两个集合中取一个数相加并对(m)取模,求一不能构造出的数。

    题解

    感觉如果(color{black}sf{s}color{red}sf{xd666})来做这题肯定能一眼秒,然而他正忙着切其他题。

    首先我们发现如果要让(a + b equiv x pmod m),如果已知(a, x),那(b)一定是唯一的。也就是说,假设给定集合是(A),与之对应的集合为(B),如果有(ain A)但找不到(bin A)使得(a + b equiv xpmod m)。那么(xin A + B)(定义(A + B = {a + b : ain A, bin B}))。反过来讲,如果(x otin A + B),那么一定能把(A)中所有元素配对(可能两个数相同),也即(x otin A + B iff A = x - A)(定义(x - A= {x - a : ain A}))。

    然后我们如果把小于(m)的整数看成一个环,如果有两个数(a, b)使(a + b equiv x pmod m)(a)顺时针时针移动,(b)肯定逆时针移动(即运动方向相反,且移动的长度应该是相等的(((a + k)mod m + (b - k)mod m equiv a + b pmod m)嘛)。

    于是我们画两个圆,都表示集合({a_i})(假设(a_i)已经排好序),我们要把第一个圆的点与第二个圆的点匹配。

    假设(a_i)(a_j)匹配。我们把(i)移动至(i+1),那么根据上面推出的单调性,(j)必须移至(j-1)(因为(a_isim a_{i+1})之间没有数了,所以(j)也只能移动一格),又因为移动距离必须相等,即(a_{i+1} - a_i = a_j - a_{j-1})

    所以我们令(b_i = a_{i} - a_{i-1})(b_1 = (a_1 - a_n)mod m)),设串(s_1 = b_nb_{n-1}b_{n-2}cdots b_1, s_2 = b_1b_2b_3cdots b_n),我们要找的是(s_1)(s_2)成环后相等,并找到一对匹配的数,他们加起来模(m)即为一组解。我们令(s_3 = s_2 + s_2),找到(s_3)中所有等于(s_1)的子串,就得到了所有解,这个问题用KMP或是Z都能解决。

    还是贴一下代码吧:

    #include <cstdio>
    #include <set>
    #include <vector>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    typedef long long LL;
    
    const int maxn = 200005;
    
    LL aa[maxn]; // 读入的a
    LL bb[maxn]; // 即上面说的b
    vector<LL> gou;
    int in[maxn << 2];
    LL Z[maxn << 2];
    set<LL> ans;
    
    int main()
    {
    	int n;
    	LL m;
    	scanf("%d%lld", &n, &m);
    	for(int i = 1; i <= n; ++i)
    		scanf("%lld", &aa[i]);
    	bb[1] = ((aa[1] - aa[n]) + m) % m;
    	for(int i = 2; i <= n; ++i)
    		bb[i] = ((aa[i] - aa[i-1]) % m + m) % m;
    	for(int i = n; i; --i) // 这里用的是Z算法,所以合并成了一个串
    	{
    		gou.push_back(bb[i]);
    		in[gou.size() - 1] = i;
    	}
    	gou.push_back(-1LL);
    	for(int i = 1; i <= n; ++i)
    	{
    		gou.push_back(bb[i]);
    		in[gou.size() - 1] = i;
    	}
    	for(int i = 1; i <= n; ++i)
    	{
    		gou.push_back(bb[i]);
    		in[gou.size() - 1] = i;
    	}
    	Z[0] = gou.size();
    	for(int i = 1, j = 1, k; i < (int) gou.size(); i = k) // Z算法
    	{
    		j = max(j, i);
    		while(gou[j] == gou[j - i])
    			++j;
    		Z[i] = j - i;
    		k = i + 1;
    		while(k + Z[k - i] < j)
    		{
    			Z[k] = Z[k - i];
    			++k;
    		}
    	}
    	for(int i = 1; i < (int) gou.size(); ++i)
    		if(Z[i] >= n) // 大力记录答案
    			ans.insert((aa[in[i] - 1 ? in[i] - 1 : n] + aa[n]) % m);
    	printf("%d
    ", (int) ans.size());
    	for(auto it = ans.begin(); it != ans.end(); ++it)
    		printf("%lld ", *it);
    	return 0;
    }
    
  • 相关阅读:
    16款优秀的Vue UI组件库推荐
    通过论证:查询字段通常返回引用,该引用可以保证是原来的对象的状态的一部分。分析变量在内存中的变化过程。
    使用XtraReport的CalculatedFiled(计算字段)实现RDLC报表中表达式
    Task:取消异步计算限制操作 & 捕获任务中的异常
    webAPI 自动生成帮助文档
    live writer 博客测试
    c#基础 函数传值
    Google,真的要离我们而去吗?
    匹配中文字符的正则表达式: [/u4e00-/u9fa5]
    希尔排序的实现
  • 原文地址:https://www.cnblogs.com/pfypfy/p/9733173.html
Copyright © 2011-2022 走看看