T1:
大意:支持尾部插入一个正整数 a i a_i ai,询问求 f ( a l , a l + 1 , . . . , a r ) f(a_l,a_{l+1},...,a_r) f(al,al+1,...,ar),其中 f ( x ) = x , f ( a 0 , a 1 . . . , a n ) = a 0 + 1 f ( a 1 , a 2 , . . . , a n ) ( n ≥ 1 ) f(x)=x,f(a_0,a_1...,a_n)=a_0+\frac 1{f(a_1,a_2,...,a_n)}(n\ge1) f(x)=x,f(a0,a1...,an)=a0+f(a1,a2,...,an)1(n≥1),输出解的最简分数形式 x y \frac xy yx中 x , y m o d 998244353 x,y \mod 998244353 x,ymod998244353后的值。
题解:
由上可知我们可以直接维护模之后的分子分母,用一个
2
×
2
2\times 2
2×2矩阵表示转移即可。线段树维护尾端插入可过。此题矩阵是有逆的,维护前缀积即可做到
O
(
n
+
m
)
O(n+m)
O(n+m),注意矩乘不一定满足交换律,但满足结合律,所以要维护从左到右乘的前缀积和从右到左乘的前缀逆,
A
n
s
[
l
,
r
]
=
M
l
−
1
−
1
∗
M
r
Ans[l,r]=M_{l-1}^{-1}*M_r
Ans[l,r]=Ml−1−1∗Mr。
Code(线段树):
#include<bits/stdc++.h>
#define maxn 1000005
using namespace std;
char cb[1<<18],*cs,*ct;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<18,stdin),cs==ct)?0:*cs++)
inline void read(int &a){
char c;while(!isdigit(c=getc()));
for(a=c-'0';isdigit(c=getc());a=a*10+c-'0');
}
const int mod = 998244353;
int n,m,N,typ,a[maxn],id[maxn],ansx,ansy;
struct Mat{
int s[2][2];
Mat(){memset(s,0,sizeof s);}
void init(int x){s[0][0]=0,s[0][1]=s[1][0]=1,s[1][1]=x;}
Mat operator * (const Mat &B)const{
Mat ret;
for(int k=0;k<2;k++) for(int i=0;i<2;i++) for(int j=0;j<2;j++)
ret.s[i][j]=(ret.s[i][j]+1ll*s[i][k]*B.s[k][j])%mod;
return ret;
}
}t[maxn<<2],ans;
void build(int i,int l,int r){
if(l==r) {id[l]=i,t[i].init(a[l]);return;}
int mid=(l+r)>>1;
build(i<<1,l,mid),build(i<<1|1,mid+1,r);
t[i]=t[i<<1|1]*t[i<<1];//!!!
}
void query(int i,int l,int r,int x,int y){
if(x<=l&&r<=y) {ans=ans*t[i];return;}
int mid=(l+r)>>1;
if(y>mid) query(i<<1|1,mid+1,r,x,y);
if(x<=mid) query(i<<1,l,mid,x,y);
}
int main()
{
freopen("alone.in","r",stdin);
freopen("alone.out","w",stdout);
read(n),read(m),read(typ);
for(int i=1;i<=n;i++) read(a[i]);
build(1,1,N=n+m);
int op,x,y,i;
while(m--){
read(op);
if(op==1){
read(x),typ&&(x^=ansx^ansy);
t[i=id[++n]].init(x);
while(i>1&&(i&1)) i>>=1,t[i]=t[i<<1|1]*t[i<<1];
}
else{
read(x),read(y),typ&&(x^=ansx^ansy,y^=ansx^ansy);
ans.s[0][0]=ans.s[1][0]=ans.s[1][1]=0,ans.s[0][1]=1,query(1,1,N,x,y);
printf("%d %d\n",ansx=ans.s[0][1],ansy=ans.s[0][0]);
}
}
}
T2:
神仙模拟费用流,还没写完。
T3:
40
%
,
n
≤
2000
40\%,n\le2000
40%,n≤2000
100
%
,
n
≤
1
0
5
100\%,n\le10^5
100%,n≤105
题解:
n
≤
2000
n\le2000
n≤2000时可以设
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示前
i
i
i个数最后一个排名为
j
j
j的合法方案数,转移时枚举上一个的排名,前缀和优化一下就是
O
(
n
2
)
O(n^2)
O(n2)。
然而这个做法并没有什么优化空间。
正解是容斥,但是钦定序列中间某个位置不满足条件的方案数并不好求,需要另类容斥。
发现DP做法的难点在于合并两个串时不好确定端点处的排名,如果通过容斥能够使得这个位置任意,那么就好做了。
先忽略所有的
<
<
<,把所有的
>
>
>看做限制
1
1
1。
那么
111...111
=
111...11
?
−
111...110
=
111...11
?
−
(
111...1
?
0
−
111...100
)
=
111...11
?
−
(
111...1
?
0
−
(
111...
?
00
−
111...000
)
)
=
111...11
?
−
(
111...
?
0
−
(
111...
?
00
−
(
11...
?
000
−
(
.
.
.
−
000...000
)
)
)
)
111...111=111...11? ~-~111...110\\ =111...11?~-~(111...1?0~-111...100)\\ =111...11?~-~(111...1?0~-(111...?00~-~111...000))\\ =111...11?~-(111...?0~-(111...?00~-~(11...?000~-~(...-000...000))))
111...111=111...11? − 111...110=111...11? − (111...1?0 −111...100)=111...11? − (111...1?0 −(111...?00 − 111...000))=111...11? −(111...?0 −(111...?00 − (11...?000 − (...−000...000))))
这样就将后半部分变成了全小于,相接处任意,这样只需要选出一部分数放后面即可。记
c
n
t
[
i
]
cnt[i]
cnt[i]表示前
i
i
i个字符有多少
>
>
>,
f
[
i
]
f[i]
f[i]表示前
i
i
i个数字的方案数,有(设
s
s
s下标从1开始,钦定
s
[
0
]
s[0]
s[0]为
>
>
>):
f
[
i
]
=
∑
j
=
0
i
−
1
[
s
j
=
=
′
>
′
]
(
−
1
)
c
n
t
[
i
−
1
]
−
c
n
t
[
j
]
∗
(
i
i
−
j
)
∗
f
[
j
]
f[i]=\sum_{j=0}^{i-1}[s_j=='>'](-1)^{cnt[i-1]-cnt[j]}*\binom i{i-j}*f[j]
f[i]=j=0∑i−1[sj==′>′](−1)cnt[i−1]−cnt[j]∗(i−ji)∗f[j]
将组合数拆开,
f
[
i
]
f[i]
f[i]变为
f
[
i
]
i
!
\frac {f[i]}{i!}
i!f[i],分治NTT即可。
Code:
#include<bits/stdc++.h>
#define maxn 300005
using namespace std;
const int mod = 998244353, G = 3;
int n,f[maxn],a[maxn],b[maxn],cnt[maxn],fac[maxn],inv[maxn];
char s[maxn];
int w[maxn],r[maxn],wlen;
inline int Pow(int a,int b){
int s=1; for(;b;b>>=1,a=1ll*a*a%mod) if(b&1) s=1ll*s*a%mod;
return s;
}
void init(int n){
wlen=w[0]=1;while(wlen<=n) wlen<<=1;
for(int i=1,g=Pow(G,(mod-1)/wlen);i<=wlen;i++) w[i]=1ll*w[i-1]*g%mod;
fac[0]=fac[1]=inv[0]=inv[1]=1;
for(int i=2;i<=wlen;i++) fac[i]=1ll*fac[i-1]*i%mod,inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
for(int i=2;i<=wlen;i++) inv[i]=1ll*inv[i]*inv[i-1]%mod;
}
void NTT(int *a,int len,int flg){
for(int i=0;i<len;i++) if(i<(r[i]=r[i>>1]>>1|(i&1?len>>1:0))) swap(a[i],a[r[i]]);
for(int i=2;i<=len;i<<=1)
for(int j=0,t=wlen/i;j<len;j+=i)
for(int k=j,o=0;k<j+i/2;k++,o+=t){
int u=a[k],v=1ll*w[flg==1?o:wlen-o]*a[k+i/2]%mod;
a[k]=(u+v)%mod,a[k+i/2]=(u-v)%mod;
}
if(flg==-1) for(int i=0,Inv=1ll*inv[len]*fac[len-1]%mod;i<len;i++) a[i]=1ll*a[i]*Inv%mod;
}
void solve(int l,int r){
if(l==r) {if(l) f[l]=1ll*f[l]*(cnt[l-1]&1?-1:1)%mod;return;}
int mid=(l+r)>>1;
solve(l,mid);
int len=1;while(len<=r-l+mid-l) len<<=1;
fill(a,a+len,0),fill(b,b+len,0);
for(int i=0;i<=r-l;i++) a[i]=inv[i];
for(int i=l;i<=mid;i++) if(s[i]=='>') b[i-l]=1ll*f[i]*(cnt[i]&1?-1:1)%mod;
NTT(a,len,1),NTT(b,len,1);
for(int i=0;i<len;i++) a[i]=1ll*a[i]*b[i]%mod;
NTT(a,len,-1);
for(int i=mid+1;i<=r;i++) f[i]=(f[i]+a[i-l])%mod;
solve(mid+1,r);
}
int main()
{
//freopen("unequal.in","r",stdin);
//freopen("unequal.out","w",stdout);
scanf("%s",s+1),n=strlen(s+1)+1;
for(int i=1;i<=n;i++) cnt[i]=cnt[i-1]+(s[i]=='>');
init(n<<1);
f[0]=1,s[0]='>',solve(0,n);
printf("%d\n",(1ll*f[n]*fac[n]%mod+mod)%mod);
}