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);
}