• codeforce刷题(六)


    1、Necklace Assembly

    给一个长度为(n)的字符串,从中挑选出一些字符然后按照你想要的顺序排成一个环,这个环顺时针旋转(k)次后与原来的环相同,问环的最大长度。

    (1<=n,k<=2000)

    题解

    旋转(k)次后依然相同,说明这个环的循环节是(k)的因数。

    由于(n,k) 比较小,可以枚举循环节的长度和环的最大长度,复杂度(O(n^2))

    int main()
    {
        int n, k, t;
        int cnt[40], tot[2005];
        string s;
    
        cin >> t;
        while(t--) {
            memset(cnt, 0, sizeof(cnt));
    
            cin >> n >> k >> s;
    
            _myfor(i, 0, n) cnt[s[i] - 'a']++;
    
            int ans = 0;
            myfor(i, 1, min(n, k)) if (k % i == 0) {   // 枚举循环节的长度
                for (int le = n; le > 0; le--) if (le % i == 0) {      // 枚举环的最大长度
                    int t = le / i, tot = 0;
                    _myfor(j, 0, 26) if (cnt[j] >= t) tot += (cnt[j] / t);
                    if (tot >= i) {
                        ans = max(ans, le);
                        break;
                    }
                }
            }
            
            cout << ans << endl;
        }
        return 0;
    }
    

    2、Task On The Board

    给一个字符串(s),长度为(m)的整数序列(b),请构造出满足如下条件的字符串(str)

    • (b[i] = sum_{str[i] < str[j]}|i-j|)

    (1 <= m <= len(s) <= 50)

    题解

    显然,如果(b[i] = 0) 说明第 (i) 位置的字符在字符串(str)中的字典序最大。

    所以我们可以不断找出(b[i])(0)的位置,然后依次填入相应的字符。

    根据已知的(b[i] = 0)的位置(原始序列(b)),不断减去这些位置上字符的贡献:(b[j] = b[j] - sum_{str[j] < str[i]}|i - j|),就能得到下一轮哪些位置((b[some\_{positon}] = 0))可以填。

    倒推

    sort(s.begin(), s.end());
    
    int n = s.size();
    
    vector<int> id;
    while(true) {
        id.clear();
        // 找b[i] == 0的位置
        myfor(i, 1, m) if (!b[i] && !use[i]) {
        	id.push_back(i);
        	use[i] = 1;
    	} 
    	if ((int)id.size() <= 0) break;
    	// 填入字符
    	char tp;
    	for (int i = n - 1; i >= 0; i--) {
    		if (cnt[s[i] - 'a'] >= (int)id.size()) {
    			tp = s[i];
    			cnt[tp - 'a'] = 0;
    			break;
    		}
    		else cnt[s[i] - 'a'] = 0;
    	}
    	// 减去上一轮那些位置的贡献
    	for (auto i: id) {
    		ans[i] = tp;
    		myfor(j, 1, m) if (b[j]) b[j] -= abs(j - i);
    	}
    }
    myfor(i, 1, m) cout << ans[i];
    cout << endl;
    

    3、Two Divisors

    (n)个数,问每个数((a_i))是否存在两个因数(d_1)(d_2),使得(gcd(d_1 + d_2,a_i) = 1)

    (1<=n<=5*10^5,2<=a_i<=1*10^7)

    题解

    已知,任意一个数都可以写成几个质因数的乘积,比如(8 = 2^3,6 = 2 * 3​。)

    那么将这些质因数分成两个不相交的集合(s_1)(s_2),令(d_1= s1)中所有数的乘积,(d_2)同理,

    (gcd(d_1 + d_2, a_i) = 1) ,所以该问题等价于求(a_i)的两个不同的质因数。

    最好用线性筛,在筛的同时求出每个合数的最小质因数。

    /*
    time:1800ms
    */
    void Inite() {
        prime[1] = 1;
        for (int i = 2; i * i <= 10000009; i++) if (!prime[i]) {
            p.push_back(i);
            for (int j = i + i; j < N; j += i) prime[j] = 1;
        }
    }
    
    int main()
    {
        Inite();
        
        cin >> n;
        myfor(i, 1, n) cin >> a[i];
    
        vector<pair<int, int> > ans;
    
        myfor(i, 1, n) {
            int tp = a[i];
            bool flag = false;
            for (auto x: p) if (a[i] % x == 0) {
                while(a[i] % x == 0) a[i] /= x;
                if (a[i] != 1) ans.push_back(make_pair(tp / a[i], a[i]));
                else ans.push_back(make_pair(-1, -1));
                flag = true;
                break;
            }
            if (!flag) ans.push_back(make_pair(-1, -1));
        }
    
        for (auto &x: ans) cout << x.first << " ";
        cout << endl;
        for (auto &x: ans) cout << x.second << " ";
        cout << endl;
    
        return 0;
    }
    

    4、Ehab and Prefix MEXs(好题)

    给一个长度为(n)的序列(a),求满足如下条件且长度为(n)的序列(b):

    • 对于任意一个(i(1<=i<=n))(MEX({b_1,b_2,···,b_i}) = a_i)

    (MEX({···}))是指:不在给定集合的最小非负数,比如(MEX({1,2,3}) = 0)(MEX{0,1,3} = 2)

    (a_i <= a_{i + 1})(0<=a_i<=i)

    题解

    构造方法:假设(a)中有(x)个不同的数(去重后统计)。如果(a[i] != a[i - 1]),那么(b[i] = a[i - 1]),因为(b[1],b[2],···,b[i - 1])都取不到(a[i -1])。这就填了(x - 1)个位置,然后剩下的(n - x + 1)个位置从小到大依次填入(a)中没有出现的数((n + 1 - x)个),刚好填完。

    困难在于怎么处理(a[i] ==a[i -1])的情况

    //code_source: https://blog.csdn.net/qq_45458915/article/details/106822285
    

    5、Game On Leaves

    张三和李四在玩游戏:给一颗有(n)个节点的无根树,和特定的节点(x)。每轮的玩家选定一个(degree <= 1)的节点并将其移除,同时删除以该节点为端点的所有边。谁先移除节点(x),则其成为赢家。在游戏过程中,他们都采取了最优的策略。问最后谁赢?(张三先手)

    题解

    Select any leaf node in the tree and remove it together with any edge which has this node as one of its endpoints. A leaf node is a node with degree less than or equal to 1.

    无根树的叶子节点的度怎么等于0? 估计是(n)可能为1吧,费解

    两个人的最优策略是:优先删除其他无关的节点,尽量将(x)留在最后。又因为每次只删除叶子节点,所以判断一下(n - 1)的奇偶性就行。

  • 相关阅读:
    gcvt(),ecvt(),fcvt()的区别
    SQLITE3 使用总结
    C++的类型转换浅析
    JAVA Class21
    JAVA Class20
    JAVA Class19
    JAVA Class18
    JAVA Class17
    JAVA Class16
    关于hover失效问题(!important)
  • 原文地址:https://www.cnblogs.com/zgglj-com/p/13266362.html
Copyright © 2020-2023  润新知