• [CF1450G] Communism


    前言

    通过钻研 ( t color{black}jcolor{red}{iangly}) 的代码,这篇博客诞生了。

    至于怎么想到这个做法的呢?那你得去请教 ( t color{black}jcolor{red}{iangly}) 了。

    题目

    CF

    洛谷

    题目大意:

    (n) 个人,每个人有一份工作,用小写字母字符串 (s) 表示,(s_i) 表示第 (i) 个人的工作。你需要通过如下操作将所有人的工作变为一样的。

    • 给定一个常数 (k=frac{a}{b}),令一种工作 (x) 的下标的集合为 ({i_1,i_2,...,i_m}(i_1<i_2<...<i_m))。如果满足 (k*(i_m-i_1+1)le m) ,那么你可以将所有的工作 (x) 改为任何一种现在存在的工作 (y)

    对于每个出现过的工作,询问是否可能最后所有人都是这个工作。

    trygub这6个字母不会出现,因为它是撒旦。

    (1le nle 5000;1le a,ble 10^5;)

    时空限制:( t 1.5s,32MB.)

    讲解

    从现在起,我将工作称为字母。

    (26-6=20),这引导我们向状压的方面去想。

    首先需要预处理每个字母出现的左右端点 (l,r) 以及数量 (cnt)

    然后考虑 (dp)。令 (dp_s) 表示字母集合为 (s) 是否满足换字母的条件,并称 (s) 可以换字母的情况为万能态。

    我们考虑如何转移。

    part1

    这类情况是 (s) 中的所有字母先全部变成相同的,然后如果 (s) 可以整体变换,那么 (s) 为万能态。

    如果存在一个字母 (iin s),并且 (s) 中的其它字母可以换字母,那么 (s) 就可以被转换成同一种字母 (i)

    在这个情况下满足题目所给条件 (k*(i_m-i_1+1)le m),那么 (s) 就为万能态。

    part2

    这类情况是讲 (s)(s) 分割成几个小块,如果每个小块都是万能态,那么 (s) 就是万能态,也就是分小块转换。

    但是如果直接暴力做显然复杂度上天,我们想一些性质。

    1.包含

    如果两种字母 (i,j) 满足 (l_i<l_j<r_j<r_i),那么显然一起换字母更优,也就是说不用考虑分开换的情况。

    2.相交

    更进一步,如果两种字母 (i,j) 满足 (l_i<l_j<r_i<r_j),一起换也一定不劣于单独换字母。

    简单说明一下:

    如果分开转换,需要满足两个条件:(k(r_i-l_i+1)le m_i,k(r_j-l_j+1)le m_j)

    而如果一起转换,只需要满足这一个条件:(k(r_j-l_i+1)le m_i+m_j),显然这个条件比上面那两个条件更容易满足。

    所以一起换也是更优的,这种情况也不用考虑分开换的情况,个人觉得这个点是个难想到的点。

    3.相离

    但是如果 (l_i<r_i<l_j<r_j),那就说不准了,有可能我们将 (i,j) 字母变为一样了之后并不是万能态,所以需要字母 (i,j) 分别都是万能态。那么我们只需要把 (s) 分割成独立的几块即可,而且由于子集已经处理过,所以其实只需要分割成独立的两块,而根据这种情况的特殊性,我们将 (s) 当中的字母按照左端点或者右端点排序,然后枚举切割点即可。

    如果存在一种分割方法,两个小块都是万能态,那么 (s) 就是万能态。

    总时间复杂度 (O(|C| imes 2^{|C|}))(C) 为出现的字符的集合。

    代码

    //12252024832524
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define TT template<typename T>
    using namespace std;
    
    typedef long long LL;
    const int MAXN = 100005;
    int n,m,A,B,anstot;
    char a[MAXN],ans[25];
    int s[MAXN];
    
    LL Read()
    {
    	LL x = 0,f = 1;char c = getchar();
    	while(c > '9' || c < '0'){if(c == '-')f = -1;c = getchar();}
    	while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
    	return x * f;
    }
    TT void Put1(T x)
    {
    	if(x > 9) Put1(x/10);
    	putchar(x%10^48);
    }
    TT void Put(T x,char c = -1) 
    {
    	if(x < 0) putchar('-'),x = -x;
    	Put1(x); if(c >= 0) putchar(c);
    }
    TT T Max(T x,T y){return x > y ? x : y;}
    TT T Min(T x,T y){return x < y ? x : y;}
    TT T Abs(T x){return x < 0 ? -x : x;}
    
    char dic[30];
    bool vis[256],dp[1 << 20];
    int l[25],r[25],cnt[25],od[25];
    
    bool cmp(int x,int y){return l[x] < l[y];}//按左端点排序
    
    int main()
    {
    //	freopen(".in","r",stdin);
    //	freopen(".out","w",stdout);
    	n = Read(); A = Read(); B = Read();
    	scanf("%s",a);
    	for(int i = 0;i < n;++ i)
    		if(!vis[a[i]])
    			dic[m++] = a[i],vis[a[i]] = 1;
    	sort(dic,dic+m);
    	for(int i = 0;i < n;++ i)
    		s[i] = lower_bound(dic,dic+m,a[i]) - dic;
    	for(int i = 0;i < m;++ i) l[i] = n,od[i] = i;
    	for(int i = 0;i < n;++ i) l[s[i]] = Min(l[s[i]],i),r[s[i]] = Max(r[s[i]],i),cnt[s[i]]++;
    	sort(od,od+m,cmp);
    	dp[0] = 1;
    	for(int S = 1;S < (1 << m);++ S)
    	{
            	//part1
    		int L = n,R = -1,C = 0,zouni = 0;
    		for(int i = 0;i < m;++ i)
    			if(S >> i & 1) 
    				L = Min(l[i],L),R = Max(r[i],R),C += cnt[i],zouni = zouni || dp[S ^ (1 << i)];
    		if(zouni && A * (R-L+1) <= C * B) dp[S] = 1;
            	//part2
    		int now = 0; 
    		for(int i = 0;i < m;++ i) //枚举切割点
    		{
    			if(S >> od[i] & 1)
    			{
    				now |= 1 << od[i];
    				dp[S] = dp[S] || (dp[S ^ now] && dp[now]);
    			}
    		}
    	}
    	for(int i = 0;i < m;++ i)
    		if(dp[(1<<m) - 1 - (1<<i)])
    			ans[++anstot] = dic[i];
    	Put(anstot); for(int i = 1;i <= anstot;++ i) putchar(' '),putchar(ans[i]);
    	return 0;
    }
    

    后记

    如果我讲的有问题,请速速打我的脸,毕竟这是我 ( t YY) 出来的做法。

  • 相关阅读:
    Lesson 43-44 Vacation Season is Approaching?
    Lesson 41-42 How would you respond?
    Lesson 37-38 Do you want to be a millionaire?
    Lesson 35-36 What did you forget?
    Lesson 33-34 Dieting
    保送
    陈老师搬书
    水题(原 USACO Mother's Milk)
    最大公约数和最小公倍数问题(luogu 1029)
    最大子矩阵(OJ 1768)
  • 原文地址:https://www.cnblogs.com/PPLPPL/p/15002786.html
Copyright © 2020-2023  润新知