2023牛客暑期多校训练营7 I-We Love Strings (分块)

文章目录

  • 题目大意
  • 题解
  • 参考代码

题目大意

官方

题解

这题给定的 n n n 大小和 s i s_i si 的总长度有玄机。
我们发现: 400 = 2 0 2 400=20^2 400=202,对于每一组数据 n n n 的个数每增加一个, s i s_i si 的平均值就会减小。
处理相同的 l l l s i s_i si
①:对于 s i ≤ 20 s_i\leq20 si20 ,完全可以暴力,枚举所有的边,复杂度为 l ∗ 2 s i l*2^{s_i} l2si
20 20 20 的范围内 最多有 20 ∗ ( s i z = 20 ) 20*(siz=20) 20siz=20) 条边 , 2 20 ∗ 20 2^{20}*20 22020 千万级别的计算。
②:对于 s i > 20 s_i>20 si>20 n < 20 n<20 n<20,考虑答案的唯一性,
如果两条边相同,则他们的贡献只有 1 1 1 ,容易得出如果某一位发生 0 0 0 1 1 1 冲突了,
他们就不是同一条边 ,如果某一位全是“ ? ? ? ”则答案的贡献 ∗ 2 *2 2
单独对于一条边,它的贡献就是 2 s u m i 2^{sum_i} 2sumi (sum为当前的问号个数)
由此我们发现了每两条边的匹配关系。总答案需要将他们去除。
a n s = a n s 1 − a n s 2 ans=ans_1-ans_2 ans=ans1ans2
显然的,这并不是最终答案,有一些边的贡献减了许多次。
这时候我们知道需要用容斥来计算结果了。
所以最终答案为 a n s = a n s 1 − a n s 2 + a n s 3 − . . . . . . ans=ans_1-ans_2+ans_3-...... ans=ans1ans2+ans3......
它的复杂度为枚举每一条边是否存在 s i ∗ 2 l s_i*2^l si2l
观察复杂度得到:
对于不同大小的 s i s_i si,我们进行分类讨论,用合适的算法计算。

参考代码

#include
#define ll long long
#define int long long
using namespace std;
const int mod=998244353;
int n;
string s[405];
char c[500],s1[500];
map<string,int> mp;
ll ans;
int cmp(string a,string b)
{
    return a.size()<b.size();
}
void dfs(int id,int x,int len,char c[500])
{
    if(x==len)                  //枚举
    {
        if(!mp[c])
            mp[c]=1,ans++;
    }
    ll a=0;
    if(s[id][x]=='?')
    {
        c[x]='1';
        dfs(id,x+1,len,c);
        c[x]='0';
        dfs(id,x+1,len,c);
    }
    if(s[id][x]=='1')
    {
        c[x]='1';
        dfs(id,x+1,len,c);
    }
    if(s[id][x]=='0')
    {
        c[x]='0';
        dfs(id,x+1,len,c);
    }
}
void work(char s1[500],int siz)           //重复的个数
{
    ll sum=1;
    int sum1=0;
    for(int i=0;i<siz;i++)
    {
        int tot=2;
        for(int j=1;j<=n;j++)
        {
            if(s1[j]=='1')
            {
                if(i==0)
                    sum1++;
            }
            else
                continue;
            int a=s[j][i]=='?'?2:s[j][i]-'0';
            if(tot==2)
                tot=a;
            else
            {
            	if(a==2)
            		tot=tot;
            	else if(tot==1 && a==0)
            		sum=0;
            	else if(tot==0 && a==1)
            		sum=0;
            	else
            		tot=a;
			}
        }
        if(tot==2){
        	sum=sum*2%mod;
		}
    }
    if(sum1==0)
        return;
//    cout<
    if(sum1&1)                 //容斥
        ans+=sum;
    else
        ans-=sum;
    ans%=mod;
    ans=(ans+mod)%mod;
}
void dfs1(int x,int len,char s1[500],int siz)
{
    if(x==len+1)
    {
//    	cout<
    	work(s1,siz);       //填数字1/0表示是否存在
    	return;
	}
    s1[x]='0';
    dfs1(x+1,len,s1,siz);
    s1[x]='1';
    dfs1(x+1,len,s1,siz);
    s1[x]='0';
}
signed main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>s[i];
    sort(s+1,s+n+1,cmp);
    int r=0,l=1;
    for(int i=0;i<=3;i++)
        s1[i]='0';
    for(int i=1;i<=400;i++)
    {
        while(r+1<=n && s[r+1].size()==i)     //找出长度
            r++;
        if(l>r)
        	continue;
//        cout<
 		if(i<=20)                 //分块
             for(int j=l;j<=r;j++)
 	           dfs(j,0,i,c);
 	    else
	        dfs1(l,r,s1,i);
        l=r+1;	 
    }
    printf("%lld\n",ans%mod);
    
}

你可能感兴趣的:(c++)