【考试总结】2022-06-07

\(a_i=1\)\(b_i\) 先进行累加得到 \(S\),剩下的 \(b_i\) 最多加一个,因为此时 \(a_i\ge 2\),加两个不如加较大的一个再做乘法

\(K=\prod_{i\in\{a_i\ge 2\}} a_i\) 那么枚举对哪个元素执行加法,答案就是 \(\max\left\{K\frac{S+b_i}{a_i}\right\}\)

Code Display
const int N=5e5+10;
int n,mxpos;
pair<int,int> a[N];
signed main(){
    scanf("%lld",&n);
    for(int i=1;i<=n;++i) scanf("%lld",&a[i].fir);
    for(int i=1;i<=n;++i) scanf("%lld",&a[i].sec);
    int S=1;
    for(int i=1;i<=n;++i) if(a[i].fir==1) S+=a[i].sec;
    long double mx=S;
    for(int i=1;i<=n;++i) if(a[i].fir!=1){
        long double cur=1.0*(S+a[i].sec)/a[i].fir;
        if(cur>mx) mx=cur,mxpos=i;
    }
    int ans=S;
    for(int i=1;i<=n;++i) if(mxpos==i) ans+=a[i].sec;
    ans%=mod;
    for(int i=1;i<=n;++i) if(a[i].fir!=1&&mxpos!=i) ans=ans*a[i].fir%mod;
    printf("%lld\n",ans);
    return 0;
}

发现逆序对数为奇数的序列只有 \(\{1,3,2\},\{2,1,3\},\{3,2,1\}\) ,那么每个元素可以匹配进其中的一个集合中,将集合的所有匹配长度的数量压到状态中进行转移即可

注意如果数量大于 \(n\) 那么一定不合法,同时如果后面的数字数量不足以将当前这些集合填满也要进行剪枝

使用 __gnu_pbds::cc_hash_table<int,int> 并将状态变成 \(20\) 进制进行存储即可

Code Display
__gnu_pbds::cc_hash_table<int,int> mp[60];
int n,pw[20];
inline vector<int> decode(int S){
    vector<int> sta;
    for(int i=1;i<=6;++i) sta.emplace_back(S%20),S/=20;
    return sta;
}
char s[100];
inline bool can(int pos,int a){return s[pos]-'0'==a||s[pos]=='0';}
bool mark=0;
int fac[30];
signed main(){
    //freopen("problem.in","r",stdin); freopen("problem.out","w",stdout);
    pw[0]=fac[0]=1;
    rep(i,1,6) pw[i]=pw[i-1]*20;
    rep(i,1,19) fac[i]=fac[i-1]*i%mod;
    int T; scanf("%lld",&T);
    while(T--){
        scanf("%lld",&n);
        scanf("%s",s+1);
        rep(i,1,3*n+1) mp[i].clear();
        mp[1][0]=1;
        for(int i=1;i<=3*n;++i){
            for(auto Sta:mp[i]){
                int S=Sta.first,v=Sta.second%mod;
                if(!v) continue;
                vector<int> sta=decode(S);
                int ned=(sta[0]+sta[2]+sta[4])*2+sta[1]+sta[3]+sta[5];
                if(3*n-i+1<ned) continue;
                if(can(i,1)){
                    if(sta[0]<n) mp[i+1][S+pw[0]]+=v;
                    if(sta[3]) mp[i+1][S-pw[3]]+=sta[3]*v;
                    if(sta[4]) mp[i+1][S-pw[4]+pw[5]]+=sta[4]*v;
                }
                if(can(i,2)){
                    if(sta[1]) mp[i+1][S-pw[1]]+=sta[1]*v;
                    if(sta[2]) mp[i+1][S-pw[2]+pw[3]]+=sta[2]*v;
                    if(sta[4]<n) mp[i+1][S+pw[4]]+=v;
                }
                if(can(i,3)){
                    if(sta[0]) mp[i+1][S-pw[0]+pw[1]]+=sta[0]*v;
                    if(sta[2]<n) mp[i+1][S+pw[2]]+=v;
                    if(sta[5]) mp[i+1][S-pw[5]]+=sta[5]*v;
                }
            }
        }
        printf("%lld\n",fac[n]*mp[3*n+1][0]%mod);
    }
    return 0;
}

\(\displaystyle S_i=\sum_{j=1}^i a_j\) 并简记 \(S=S_n\)

移动次数本质是前缀和的差的绝对值,枚举 \(b\) 序列的前缀和可以得到如下算式:

\[\sum_{i=1}^{n-1}w_i\sum_{s=0}^S\binom{i+s-1}{i-1}\binom{S-s+n-i-1}{n-i-1}|s-S_i| \]

单独考察每个 \(i\) ,将绝对值展开可以得到:

\[2\sum_{s=0}^{S_i}(s_i-s)\binom{i+s-1}{i-1}\binom{S-s+n-i-1}{n-i-1}+\sum_{s=0}^{S}(s-s_i)\binom{i+s-1}{i-1}\binom{S-s+n-i-1}{n-i-1} \]

考虑加号后半部分,将括号展开后考虑并用吸收恒等式将 \(j\) 吸入组合数中

注意到从 \((0,0)\)\((n,m)\) 的路径条数有另一个计算方式,即枚举在第 \(i\) 行/列经过的另一维度的下标,那么套用之可以化简得到:

\[\sum_{s=0}^{S}(s-s_i)\binom{i+s-1}{i-1}\binom{S-s+n-i-1}{n-i-1}=i\binom{n+S-1}{S-1}-S_i\binom{S+n-1}{n-1} \]

即减号前是从 \((0,0)\) 走到 \((n,S-1)\) ,减号后走到的是 \((n-1,S)\)

直接套入每个 \(i\)\(w_i\) 的系数表达式的加号前一部分发现这就是钦定经过某行/列时另一维度标号小于某值的方案数

此时使用组合意义可以将 \(p\leftarrow p+1,q\leftarrow q+1\),也就是在指针 \(p,q\) 变化的过程中添加从第 \(q+1\) 行走或者减去从第 \(p\) 行走的方案即可

在实际问题的解决中 \(i,s_i\) 都是不降的,均摊复杂度到了 \(\Theta(n+m)\)

Code Display
const int N=3e6+10;
int fac[N],ifac[N],n;
inline int binom(int n,int k){return n<k?0:mul(mul(fac[n],ifac[k]),ifac[n-k]);}
struct Calculator{
	int p,q,n,m,res;
	inline void init(int N,int M){
		n=N; m=M;
		p=q=0;
		res=binom(n+m-1,n-1);
	}
	inline void move(int x,int y){
		while(q<y){
			++q;
			ckadd(res,mul(binom(p+q,p),binom(n+m-p-q-1,m-q)));
		}
		while(p<x){
			++p;
			ckdel(res,mul(binom(p+q,p),binom(n+m-p-q-1,n-p)));
		}
		return ;
	}
}calc1,calc2;
signed main(){
	n=3e6; fac[0]=1;
	for(int i=1;i<=n;++i) fac[i]=mul(fac[i-1],i);
	ifac[n]=ksm(fac[n],mod-2);
	for(int i=n;i>=1;--i) ifac[i-1]=mul(ifac[i],i);
	int T; scanf("%lld",&T);
	while(T--){
		scanf("%lld",&n);
		vector<int>a(n+1),w(n);
		for(int i=1;i<=n;++i){
			scanf("%lld",&a[i]);
			a[i]+=a[i-1];
		}
		int ans=0;
		calc1.init(n-1,a[n]);
		calc2.init(n,a[n]-1);
		for(int i=1;i<n;++i){
			scanf("%lld",&w[i]);
			int delt=0;
			delt+=i*binom(n+a[n]-1,a[n]-1);
			delt-=a[i]*binom(n+a[n]-1,n-1);
			if(a[i]){
				calc1.move(i-1,a[i]);
				calc2.move(i,a[i]-1);
				delt+=2*(a[i]*calc1.res-i*calc2.res);
			}
			delt=(delt%mod+mod)%mod;	
			ckadd(ans,mul(delt,w[i]));
		}
		printf("%lld\n",ans);
	}
	return 0;
}

答案的下界就是让 \(T_i=S[i,2i]\),数量自然是 \(\left\lfloor\frac{n}2\right\rfloor\)

但是对于一个在 \(S\) 中出现超过一次时的子串 \(S[l,r]\) 可以不论长度得将数量加到 \(r-l+1\) ,这个过程可以倒过来考察: 从较右的出现 \([c,d]\) 开始缩头尾,如果头到了 \(a\) 那么再挪到 \(c\) 继续缩

那么缩完了还可以再扩展到 \(n\) ,所以找到最靠左的一次结尾位置 \(r\) 即可得到结果 \(len+\dfrac{n-r}2\)

使用 \(\rm SAM\) 进行上述过程的维护即可,注意特判没有子串出现两次的情况

Code Display
const int N=1e6+10;
vector<int> G[N];
char s[N];
int son[N][26],len[N],fa[N],pos[N],siz[N],tot=1,las=1,n;
inline void extend(int x){
	int tmp=las,np=las=++tot;
	pos[np]=len[np]=len[tmp]+1; siz[np]=1;
	while(tmp&&!son[tmp][x]) son[tmp][x]=np,tmp=fa[tmp];
	if(!tmp) return fa[np]=1,void();
	int q=son[tmp][x];
	if(len[q]==len[tmp]+1) return fa[np]=q,void();
	int clone=++tot; len[clone]=len[tmp]+1;
	fa[clone]=fa[q]; fa[q]=fa[np]=clone;
	rep(i,0,25) son[clone][i]=son[q][i];
	while(son[tmp][x]==q) son[tmp][x]=clone,tmp=fa[tmp];
	return ;
}
int main(){
	int T; scanf("%d",&T);
	while(T--){
		scanf("%s",s+1); 
		n=strlen(s+1);
		for(int i=1;i<=n;++i) extend(s[i]-'a');
		for(int i=2;i<=tot;++i) G[fa[i]].emplace_back(i);
		int ans=n/2;
		function<void(int)>dfs=[&](int x){
			for(auto t:G[x]){
				dfs(t);
				siz[x]+=siz[t];
				if(!pos[x]||pos[x]>pos[t]) pos[x]=pos[t];
			}
			if(siz[x]>1) ckmax(ans,(n-pos[x])/2+len[x]);
			return ;
		};
		dfs(1);
		printf("%lld\n",ans);
		rep(i,1,tot){
			G[i].clear();
			rep(j,0,25) son[i][j]=0;
			siz[i]=fa[i]=len[i]=pos[i]=0;
		}
		tot=las=1;
	}
	return 0;
}

posted @ 2022-06-07 16:52  yspm  阅读(379)  评论(23编辑  收藏  举报