修仙录 3.13

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_41709770/article/details/88533130

有鲍丽芬啦拉拉啊安啦安啦
第一题暴力竟然95(还是WA掉的),水水水水乐乐乐乐


jzoj 6056 碱基配对

https://jzoj.net/senior/#contest/show/2665/0
水过啦哈哈哈
n 2 n^2 暴力水过啦
枚举p后跑一遍A串,因为需要比较的区间是长度不变而向右移动的,每次加上右端点,减去左端点就好了
正解如下:
对于 100%的数据,考虑枚举每个字符,用 ai表示 Ai-k到 Ai+k中是否出现过这个字符,用 bi表示 Bi是否
为这个字符,则在第 p 位能匹配到的数量为 b 0 a p + b 1 a p + 1 + . . . + b m 1 a p + m 1 b_0*a_{p}+b_1*a_{p+1}+...+b_{m-1}*a_{p+m-1}
不难发现反转 a 数组后即为一个卷积。
于是可以用 FFT 或 NTT 快速求出这个字符在每一位能匹配到的数量,之后枚举每一位并判断 4种字符能匹配到的数量和是否为 m 即可,时间复杂度 O(nlogn)。
是可以打的

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int MAXN=2e5+5;

int k,n,m;
char A[MAXN],B[MAXN];
int sum[4],l,r;
int ans;

int f(char c){
	if(c=='Z') return 0;
	if(c=='P') return 1;
	if(c=='S') return 2;
	if(c=='B') return 3;
}

int main(){
	freopen("base.in","r",stdin);
	freopen("base.out","w",stdout);
	cin>>k>>A>>B;
	n=strlen(A),m=strlen(B),ans=n-m+1;
	if(n<=3000)
	for(int p=0;p<=n-m;p++){
		memset(sum,0,sizeof(sum));
		l=r=0,sum[f(A[0])]++;
		while(r<m+k+p){
			int i=r-p-k;
			if(i>=m) break;
			if(i>=0&&!sum[f(B[i])]) {ans--;break;}
			r++;
			if(r<n) sum[f(A[r])]++;
			if(r-l>k*2) sum[f(A[l++])]--;
		}
	}
	cout<<ans;
	return 0;
} 

jzoj 6058 false-false-true

https://jzoj.net/senior/#contest/show/2665/2
这道题强到我无法用言语形容。
首先要知道,每次回答所使用的最优决策是回答剩下更多的那个(答对的概率显然更大)
这样看来不确定的就只有真正的答案了,从而导致被嘲讽的次数不同
这时,出现了一个光头神奇的想法
如果把true看成向右走,把false看成向上走,那么所有答案的情况组成的就是个网格图
而每条S到T的路径都代表了一种答案的可能性
word文档画图累死人
在这里插入图片描述如图,从S到T的那条红色路径就是一种答案情况(FTTFTTTTTFFTF)
而蓝线表示剩下的F和T数量相同的情况情况,这时根据最优策略,我们是随便选F或T,就有 1 2 \frac{1}{2}
概率选错,即黄点右边的那条边边答错概率是 1 2 \frac{1}{2}
除开这种边后
对于蓝线左边,T多于F,一定选T,这样答错的位置是:1,4
对于蓝线右边,F多于T,一定选F,这样答错的位置是:9,12

可以发现,所有情况,一定可以答对n个(蓝线左边的横线+右边的竖线==n,把他们翻到一边很显然的)
再换一下思路:把错的题的期望转化成(n+m-对的题的期望)
所以最终我们要求的就是:m-黄点右边的边中答对的期望
这下就直接枚举黄点的数量,通过组合数算出所有的分布情况,乘上 1 2 \frac{1}{2} 答对概率,除以总方案数,就是答案啦
代码和思维难度根本不是一个级别的QAQ

Ps:逆元竟然可以递推求666
https://blog.csdn.net/Frods/article/details/53868810

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int MAXN=1e6+5;
const int mod=998244353;

int n,m;
long long mi[MAXN],ny[MAXN],ans;

long long C1(int r,int k){
	return mi[r]*ny[k]%mod*ny[r-k]%mod;
}

long long C2(int r,int k){
	return mi[k]*mi[r-k]%mod*ny[r]%mod;
}

int main(){
	freopen("fft.in","r",stdin);
	freopen("fft.out","w",stdout);
	cin>>n>>m; if(n<m) swap(n,m);
	mi[0]=mi[1]=1; for(int i=2;i<=n+m;i++) mi[i]=mi[i-1]*i%mod;
	ny[0]=ny[1]=1; for(int i=2;i<=n+m;i++) ny[i]=(mod-mod/i)*ny[mod%i]%mod;
	for(int i=1;i<=n+m;i++) ny[i]=ny[i]*ny[i-1]%mod;
	for(int i=1;i<=m;i++) ans=(ans+C1(n-i+m-i,m-i)*C1(i<<1,i)%mod*ny[2]%mod*C2(n+m,m)%mod)%mod;
	cout<<(m-ans+mod)%mod;
	return 0;
}

第二题改不动的。

猜你喜欢

转载自blog.csdn.net/qq_41709770/article/details/88533130