2024年多校联考公益周赛第29场(提高级)

Posted on 2024-09-03 22:29  _XOFqwq  阅读(7)  评论(0编辑  收藏  举报

赛时:\(0+0+0\)

补题:\(100+100+0\)

T1

hash 即可。

code
#include<bits/stdc++.h>
#define ull unsigned long long
using namespace std;

const int N=1e4+5;
const int P=13331;
string s,t;
int m,ss,tt,ans;
ull ps[N],pt[N],hss[N],hst[N];

void hss_init(){
    ps[0]=1;
	for(int i=1;i<=ss;i++){
		hss[i]=hss[i-1]*P+s[i];
        ps[i]=ps[i-1]*P;
    }
}
void hst_init(){
	pt[0]=1;
	for(int i=1;i<=tt;i++){
		hst[i]=hst[i-1]*P+t[i];
		pt[i]=pt[i-1]*P;
	}
}
ull hss_get(int l,int r){
	return hss[r]-hss[l-1]*ps[r-l+1];
}
ull hss_del(int x){
	return hss_get(1,x-1)*ps[ss-x]+hss_get(x+1,ss);
}
ull hst_get(int l,int r){
	return hst[r]-hst[l-1]*pt[r-l+1];
}
ull hst_del(int x){
	return hst_get(1,x-1)*pt[tt-x]+hst_get(x+1,tt);
}

int main(){
	ios::sync_with_stdio(0);
	cin>>s>>m;
	ss=s.size();
	s="#"+s;
	hss_init();
	while(m--){
		cin>>t;
		tt=t.size();
		t="#"+t;
		hst_init();
        if(hss[ss]==hst[tt]){
            ans++;
            continue;
        }
		bool f=0;
        if(ss<tt){
		    for(int i=1;i<=tt;i++){
			    if(hst_del(i)==hss[ss]){
				    f=1;
				    break;
			    }
		    }
        }
        else{
            for(int i=1;i<=ss;i++){
			    if(hss_del(i)==hst[tt]){
				    f=1;
				    break;
			    }
		    }
        }
		if(f)
			ans++;
	}
	cout<<ans;
	return 0;
}

考场寄因:没开 freopen,并且 没提交

T2

数数题。

首先题目应该是有个错误,就是要求应该是漂亮值 \(\ge d\)

考虑边界情况:

  • \(k=0\),答案为 \(n!\)

  • \(k=n\),若 \(d=0\),答案为 \(n!\),否则为 \(0\)

接着,我们考虑将 \(>k\) 的数称为 II 类数,其余称为 I 类数。

不妨将连在一起的同一类数看成一个数(一个联通块),这样答案只要在乘上 \(k!(n-k)!\)(就是它们内部的排列总数) 即可。

令总联通块个数为 \(p\)。显然,一个联通块若有 \(n\) 个 II 类数,则其贡献即为 \(n-1\)\(p\) 个联通块的总贡献为 II 类元素个数 \(- \ p\),即 \(n-k-p\)

由题,\(n-k-p \ge d \to n-k-d \ge p\),得到 \(p \in [1,n-k-d]\),于是枚举 \(p\) 再进行计算。

对于每一个 \(p\),我们可以选出一个 I 类数(反正不会影响答案)单独作为一个联通块,贡献为 \(n\);然后 II 类数 与 I 类数 就交替形成联通块,使用插板法即可求解分的方案数,贡献即为 \(\dfrac{n \times C^{k-1}_{p-1} \times C^{n-k-1}_{p-1}}{p}\)(第一个操作相当于删去了一个联通块,对于每一种方案,都有 \(p\) 中删除方式,而只能对应一个,因此要除以 \(p\)),简单画一下图便可理解上式。

组合数预处理一下阶乘、逆元和阶乘逆元就可以 \(O(1)\) 求出了。

正好有点忘了,补充一下怎么线性求逆元。

\(i\)\(\bmod \ p\) 意义下的逆元:

\(k = \lfloor \frac{p}{i} \rfloor,r=p \bmod i\)

于是有 \(k \times i + r \equiv 0(\bmod \ p)\)

移项:\(k \times i \equiv -r(\bmod \ p)\)

两边 \(\times \ i^{-1} \times r^{-1}\)\(k \times r^{-1} \equiv -i^{-1}(\bmod \ p)\)

移项:\(i^{-1} \equiv -k \times r^{-1}(\bmod \ p)\)

替换:\(i^{-1} \equiv -\lfloor \frac{p}{i} \rfloor \times (p \bmod i)^{-1}\)

为保证 \(i^{-1}\) 为正,右边 \(+ \ p\)\(i^{-1} \equiv (p -\lfloor \frac{p}{i} \rfloor) \times (p \bmod i)^{-1}\)

然后就可以递推了。

code
#include<bits/stdc++.h>
#define int long long
using namespace std;

const int N=1e6+5;
const int MOD=998244353;
int t,n,k,d;
int fac[N],inv[N],inv_fac[N];

void fac_init(){
	fac[0]=inv[1]=inv_fac[0]=1;
	for(int i=1;i<N;i++)
		fac[i]=fac[i-1]%MOD*i%MOD;
	for(int i=2;i<N;i++)
		inv[i]=(MOD-MOD/i)*inv[MOD%i]%MOD;
	for(int i=1;i<N;i++)
		inv_fac[i]=inv_fac[i-1]%MOD*inv[i]%MOD;
}
int C(int x,int y){
    if(y<0||x<y)
        return 0;
	return fac[x]%MOD*inv_fac[y]%MOD*inv_fac[x-y]%MOD;
}

signed main(){
	ios::sync_with_stdio(0);
	cin>>t;
	fac_init();
	while(t--){
		cin>>n>>k>>d;
		if(!k){
			cout<<fac[n]<<'\n';
			continue;
		}
		else if(k==n){
			if(!d)
                cout<<fac[n]<<'\n';
            else
                cout<<0<<'\n';
			continue;
		}
		int ans=0;
		for(int p=1;p<=n-k-d;p++){
            if(p==1)
                ans=(ans+n)%MOD;
            else
			    ans=(ans+n%MOD*C(k-1,p-1)%MOD*C(n-k-1,p-1)%MOD*inv[p]%MOD)%MOD;
        }
		ans=ans%MOD*fac[k]%MOD*fac[n-k]%MOD;
		cout<<ans<<'\n';
	}
	return 0;
}

考场寄因:没有考虑 \(\ge d\) 这个条件。

T3

skip,不补了。

总结

  • 要持之以恒地打周考,才能避免保龄的情况。

  • 审题。