Beautiful Bracket Sequence (hard version)
题意:给出一个包含的字符串,求将变为之一的所有方案中,字符串的深度和。深度为其子序列中最长的为的字符串其中前位都是后位都是。
发现一个性质这题就很好做了。
对于一个只有的字符串,一定只有一个点,满足它左边的左括号数等于的右括号数,并且这个字符串的深度就是它左边的左括号数。
那么我们枚举每个位置,假设左边有个左括号,有个问号,右边有个右括号,个问号。
则答案为
发现因为组合数下标为0或者大于上标都为,可以把式子写成枚举范围易于范德蒙德卷积的形式:
#include<bits/stdc++.h>
#define maxn 1000006
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define mod 998244353
using namespace std;
int n,fac[maxn],inv[maxn],invf[maxn];
char s[maxn];
int C(int a,int b){ if(a<0||b<0||a<b) return 0;return fac[a] * 1ll * invf[b] % mod * invf[a-b] % mod; }
int main(){
scanf("%s",s);
n=strlen(s);
fac[0]=fac[1]=inv[0]=inv[1]=invf[0]=invf[1]=1;
rep(i,2,n) fac[i] = 1ll * fac[i-1] * i % mod , inv[i] = 1ll * (mod - mod / i) * inv[mod % i] % mod,
invf[i] = 1ll * invf[i-1] * inv[i] % mod;
int a=0,c=0,y=0,z=0;
rep(i,0,n-1){
if(s[i] == ')') y++;
if(s[i] == '?') z++;
}
int ans = 0;
rep(i,0,n-1){
if(s[i] == '(') a ++;
else if(s[i] == ')') y --;
else c ++ , z -- ;
ans = (ans + 1ll * a * C(z+c,z+y-a) + 1ll * c * C(z+c-1,z+y-a-1)) % mod;
}
printf("%d\n",(ans+mod)%mod);
}
CF1286D LCC
数轴上有个粒子,每个有的初速度,有的机率往左运动(有距离往右),求第一次碰撞发生的时间的期望值,如果没有碰撞则为。
发现第一次碰撞一定发生在相邻的点间。
把相邻的点间可能的种碰撞形式都求出来,按照碰撞时间排序,那么就相当于每次禁用一种碰撞然后求概率,发现这个求概率是一个,表示是往左还是往右的概率。
可以写矩阵乘积动态维护。
设表示前为,后为可行的概率。
则
用线段树实现了一发,注意区间积超过的位置也要赋初值。
#include<bits/stdc++.h>
#define maxn 400005
#define db double
#define mod 998244353
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define LL long long
#define Ct const
using namespace std;
int N;
int n,p[maxn],c[maxn],sx[maxn],ty[maxn],px[maxn],py[maxn],cnt,rtm[maxn];
int x[maxn],v[maxn];
db TM[maxn];
bool cmp(Ct int &u,Ct int &v){ return TM[u] < TM[v]; }
int Pow(int b,int k){ int r=1;for(;k;k>>=1,b=1ll*b*b%mod) if(k&1)r=1ll*r*b%mod;return r; }
struct mat{
int a[2][2];
mat (int d=0){memset(a,0,sizeof a);rep(i,0,1) a[i][i]=d;}
mat operator *(Ct mat &B)Ct{
mat r;
r.a[0][0] = (1ll * a[0][0] * B.a[0][0] + 1ll * a[0][1] * B.a[1][0]) % mod;
r.a[0][1] = (1ll * a[0][0] * B.a[0][1] + 1ll * a[0][1] * B.a[1][1]) % mod;
r.a[1][0] = (1ll * a[1][0] * B.a[0][0] + 1ll * a[1][1] * B.a[1][0]) % mod;
r.a[1][1] = (1ll * a[1][0] * B.a[0][1] + 1ll * a[1][1] * B.a[1][1]) % mod;
return r;
}
}tr[maxn];
int main(){
scanf("%d",&n);
int iv = Pow(100 , mod-2);
rep(i,1,n) scanf("%d%d%d",&x[i],&v[i],&p[i]),p[i]=1ll*p[i]*iv%mod;
for(N=1;N<n;N<<=1);
rep(i,1,N)
rep(j,0,1) rep(k,0,1)
tr[N+i-1].a[j][k] = (k ? p[i] : 1 - p[i]);
per(i,N-1,1) tr[i] = tr[i<<1] * tr[i<<1|1];
rep(i,2,n) rep(j,0,1) rep(k,0,1){
int vx = j ? v[i-1] : -v[i-1] , vy = k ? v[i] : -v[i];
if(vx <= vy) continue;
sx[++cnt] = i-1 , ty[cnt] = i;
px[cnt] = j , py[cnt] = k;
TM[cnt] = 1.0 * (x[i] - x[i-1]) / (vx - vy);
rtm[cnt] = (x[i] - x[i-1]) * 1ll * Pow(vx - vy , mod-2) % mod;
c[cnt] = cnt;
}
sort(c+1,c+1+cnt,cmp);
int ans = 0;
rep(i,1,cnt){
int u = c[i];
mat tmp = tr[ty[u] + N - 1];
tr[ty[u] + N - 1] = mat();
tr[ty[u] + N - 1].a[px[u]][py[u]] = tmp.a[px[u]][py[u]];
for(int p=(ty[u]+N-1)>>1;p;p>>=1) tr[p] = tr[p<<1] * tr[p<<1|1];
ans = (ans + 1ll * rtm[u] * (tr[1].a[0][0] + tr[1].a[0][1])) % mod;
tmp.a[px[u]][py[u]] = 0;
tr[ty[u] + N - 1] = tmp;
for(int p=(ty[u]+N-1)>>1;p;p>>=1) tr[p] = tr[p<<1] * tr[p<<1|1];
}
printf("%d\n",(ans+mod)%mod);
}
CF1267G Game Relics
题解:
这个题的随机购买操作是在所有的物品中随机,所以可以发现随机操作一定是越来越不优秀的。
而且因为给出了,也就是说第一步随机一定最优。
可以猜想:一定是先一直随机,到某个时刻后就开始购买所有的物品。
具体证明,咕咕咕了因为随机操作一定是越来越不优秀的,一旦某个时刻选择购买,后面也肯定选择购买。
但是我们不太好处理随机到已经拥有了哪些物品再开始购买。
所以我们把购买看做是在未拥有的物品中随机一个买。
那么实际上就是每个时刻有两种随机策略,选期望值小的一方,
最后总花费的期望值就是每个时刻购买花费到达这个时刻的概率。
#include<bits/stdc++.h>
#define maxn 105
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define db double
using namespace std;
int n,c[maxn];
db f[maxn][10005],C[maxn][maxn],x;
int main(){
scanf("%d%lf",&n,&x);
int sm = 0;
f[0][0] = 1;
rep(i,1,n){
scanf("%d",&c[i]);
sm += c[i];
per(j,i,1) per(k,sm,c[i])
f[j][k] = (f[j][k] + f[j-1][k-c[i]]);
}
db ans = 0;
rep(i,C[0][0]=1,n) rep(j,C[i][0]=1,i)
C[i][j] = C[i-1][j-1] + C[i-1][j];
rep(i,0,n-1) rep(k,0,sm)
ans += f[i][k] / C[n][i] * min((sm-k) * 1.0 / (n-i) , (1.0 * n / (n-i) + 1) * x / 2);
printf("%.10lf\n",ans);
}
CF1081G Mergesort Strikes Back
给出求对长度为的随机排列,做归并排序,但是只递归层,求逆序对的期望个数。
题解:递归层后的区间因为不会排序直接就是随机区间,假设其长度为,那么期望逆序对个数就是。
考虑对无序的两个序列做归并会发生什么。
如果我们是要从小到大的顺序。
发现实际上在某个序列是前一个大于后一个,那么前一个被选出来之后后一个马上也被选。
实际上是一个被选出来之后后面小于等于它的都会被依次选出来。
所以就是以前缀最大值来将无序的序列分成若干块,归并前后块头都是有序的,块头以外的数就像是块头的附赠品一样顺序完全不会变。
所以对于两个不在同一个区间的数,他们的块头分别是他们的前缀最大值,
发现如果这两个前缀最大值中较大的那个也就是他们前面的所有数中最大的那个是这两个数之一,就完全不可能构成逆序对。
否则这两个数块头不知道谁大,数也不知道谁大,贡献逆序对的概率就是。
所以如果第一个数前有个数,第二个数前有个数,他们之间是逆序对的期望就是。
一个长度为的区间和长度为的区间的贡献就是
这个题因为底层长度只有两种所以怎么做都能过。
#include<bits/stdc++.h>
#define fi first
#define se second
#define mkp make_pair
#define pb push_back
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
const int N=1e5+10;
int n,k,mod,ans;
int inv[N],sum[N];
map<int,int>cnt;
map<int,int>::iterator it1,it2;
int read()
{
int ret=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=0;c=getchar();}
while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
return f?ret:-ret;
}
void up(int &x,int y){x+=y;if(x>=mod)x-=mod;if(x<0)x+=mod;}
int Pow(int b,int k){ int r=1;for(;k;k>>=1,b=1ll*b*b%mod) if(k&1) r=1ll*r*b%mod; return r; }
void divide(int l,int r,int dp){
if(dp<=1 || l==r){cnt[r-l+1]++;return;}
int mid=(l+r)>>1;
divide(l,mid,dp-1);divide(mid+1,r,dp-1);
}
int calc(int x,int y){
int res=(ll)x*y%mod;
for(int i=1;i<=x;++i) up(res,-(sum[i+y]-sum[i])*2%mod);
return res;
}
int main(){
n=read();k=read();mod=read();
for(int i=1;i<N;++i) sum[i]=inv[i]=Pow(i,mod-2),up(sum[i],sum[i-1]);
divide(1,n,k);
for(it1=cnt.begin();it1!=cnt.end();++it1)
{
int t=it1->fi,s=it1->se;
up(ans,(ll)t*(t-1)%mod*inv[2]%mod*s%mod);
up(ans,(ll)s*(s-1)%mod*inv[2]%mod*calc(t,t)%mod);
}
for(it1=cnt.begin();it1!=cnt.end();++it1)
for(it2=cnt.begin();it2!=cnt.end();++it2)
{
int x=it1->fi,y=it2->fi;
if(x>=y) continue;
up(ans,(ll)calc(x,y)*it1->se%mod*it2->se%mod);
}
printf("%d\n",(ll)ans*inv[2]%mod);
}
「SDOI2017」龙与地下城
有个概率均匀取值在的离散变量。
求的概率。
容易发现当很大的时候这个概率我们可以抽象成正态分布。
出题人给出了其期望为,方差为
等等为什么个变量和的方差只需要?
为了方便调用库函数,我们将这个正态分布化为也即标准正态分布。
具体的,我们就是要求在的和。
的概率分布函数:
换一下元
则原式变为
(因为的范围缩小了倍所以要乘上。)
所以原式为,大范围直接计算。
对于小范围的数据不一定是很符合正态分布的,
但是我们发现小范围就直接是一个多项式快速幂,
发现这个底数多项式的次数才,
这放在2020年还写FFT就有点问题了
直接求多项式快速幂即可。
#include<bits/stdc++.h>
#define db double
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
using namespace std;
db X,Y,mu,sig;
#define maxn 2500
db A[maxn],B[maxn],sm[maxn];
int main(){
int T;
for(scanf("%d",&T);T--;){
scanf("%lf%lf",&X,&Y);
if(X * Y <= 2000){
B[0] = pow(1/X,Y);
rep(i,1,X*Y-1){
B[i] = 0;
rep(j,1,min((int)X-1,i))
B[i] += Y * j * B[i-j] - (i-j) * B[i-j];
B[i] /= i;
}
rep(i,0,X*Y-1) sm[i] = (i ? sm[i-1] : 0) + B[i];
for(int tim=1;tim<=10;tim++){
int a,b;scanf("%d%d",&a,&b);
printf("%.10lf\n",sm[b] - (a ? sm[a-1] : 0));
}
}
else{
mu = (X-1) / 2 * Y , sig = sqrt((X*X-1) / 12 * Y);
for(int tim = 1;tim <= 10;tim++){
int A,B;
scanf("%d%d",&A,&B);
printf("%.10lf\n",(erf((B - mu) / sig / sqrt(2)) - erf((A - 1 - mu) / sig / sqrt(2))) / 2);
}
}
}
}
CodeForces - 506E Mr. Kitayuta’s Gift
#include<bits/stdc++.h>
#define maxn 205
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define vi vector<int>
#define pb push_back
#define mod 10007
#define Ct const
using namespace std;
int f[maxn * 10][maxn][maxn];
char s[maxn];
int n;
int Pow(int b,int k){ int r=1;for(;k;k>>=1,b=1ll*b*b%mod) if(k&1)r=1ll*r*b%mod;return r; }
vi BM(vi &a){
vi r(1,1),p,t;int l;
rep(i,0,a.size()-1){
int b=0;
rep(j,0,r.size()-1) b = (b + 1ll * r[j] * a[i-j]) % mod;
if(!b) continue;
t = r , r.resize(max(r.size() , i+2 - (r.size() - (r.size() != 0))));
rep(j,0,p.size()-1) r[i-l+j] = (r[i-l+j] - 1ll * p[j] * b) % mod;
p = t;int iv = Pow(b , mod-2);
rep(j,0,p.size()-1) p[j] = 1ll * p[j] * iv % mod;
l = i;
}
return r;
}
typedef vi poly;
poly Mul(const poly &A,const poly &B,const poly &P){
poly r(A.size() + B.size() - 1);
rep(i,0,A.size()-1) rep(j,0,B.size()-1) r[i+j] = (r[i+j] + 1ll * A[i] * B[j]) % mod;
per(i,r.size()-1,P.size()-1) if(r[i]){// in this problem P[P.size()-1] = 1 , so there is no need to getinv.
int t = r[i];
rep(j,1,P.size())
r[i-j+1] = (r[i-j+1] - 1ll * P[P.size()-j] * t) % mod;
}
r.resize(P.size()-1);
return r;
}
int main(){
scanf("%s",s+1);
n = strlen(s+1);
f[0][0][n+1] = 1;
rep(i,0,7*n) rep(j,0,n) per(k,n+1,j+1) if(f[i][j][k]){
if(s[j+1] == s[k-1]){
if(j + 1 <= k - 1){
f[i][j+1][k-1] = (f[i][j+1][k-1] + f[i][j][k]) % mod;
f[i+2][j][k] = (f[i+2][j][k] + 25ll * f[i][j][k]) % mod;
}
else
f[i+2][j][k] = (f[i+2][j][k] + 26ll * f[i][j][k]) % mod;
if(j + 1 == k - 1)
f[i+1][j][k-1] = (f[i+1][j][k-1] + f[i][j][k]) % mod;
}
else{
if(j + 1 <= k - 1){
f[i+2][j][k] = (f[i+2][j][k] + 24ll * f[i][j][k]) % mod;
f[i+1][j+1][k] = (f[i+1][j+1][k] + f[i][j][k]) % mod;
f[i+1][j][k-1] = (f[i+1][j][k-1] + f[i][j][k]) % mod;
}
else
f[i+2][j][k] = (f[i+2][j][k] + 26ll * f[i][j][k]) % mod;
}
}
vi a;
rep(i,0,7*n){
int sm = 0;
rep(j,1,n) sm = (sm + f[i][j][j]) % mod;
rep(j,0,n) sm = (sm + f[i][j][j+1]) % mod;
if(i) rep(j,0,n) sm = (sm + f[i-1][j][j+1] * 26ll) % mod;
a.push_back(sm);
}
vi P = BM(a);
reverse(P.begin(),P.end());
poly r(1,1),t(2,0);
t[1] = 1;
int m;scanf("%d",&m);
for(;m;m>>=1,t=Mul(t,t,P)) if(m&1)
r = Mul(r,t,P);
int ans = 0;
rep(i,0,r.size()-1) ans = (ans + 1ll * r[i] * a[i]) % mod;
printf("%d\n",(ans+mod)%mod);
}