前言
200pts
40+100+60
rnk3
拿到牌勒嘿嘿嘿(脑补流口水黄豆)
T3两个log想在ybt的机子上过5e5确实是奢望了。
把串反过来改一改dp定义看出题解的那个性质就可以拿掉那个log,有些可惜。
但毕竟没有挂分,还是不错的=v=
题目解析
随机减法(calculate)
乍一看这个题就感觉在 OI-wiki 上见过。
但是就记得在OIwiki上有了,属于那个章节,怎么做统统不记得…
于是就只配打暴力了…
其实也看出了卷积,但那个东西需要再变成封闭形式化一下,必然还是脱离不了
O
(
k
)
O(k)
O(k) 的复杂度。
而且看到
1
e
9
+
7
1e9+7
1e9+7 这种模数本能的觉得不是多项式…
而且为啥我的dp转移和题解又不一样,难看的很难化了…
题目的贡献其实也就是
∏
a
i
\prod a_i
∏ai 的变化量。
即:
∏
a
i
−
∏
a
i
′
\prod a_i-\prod a_i'
∏ai−∏ai′
前面已知,考虑如何算后面的贡献。
设每个数的删除次数为
d
1...
n
d_{1...n}
d1...n,则有:
a
n
s
=
∑
∑
d
i
=
k
k
!
∏
(
a
i
−
d
i
)
∏
d
i
!
ans=\sum_{\sum d_i=k}\frac{k!\prod(a_i-d_i)}{\prod d_i!}
ans=∑di=k∑∏di!k!∏(ai−di)
=
k
!
∑
∑
d
i
=
k
∏
(
a
i
−
d
i
)
∏
d
i
!
=k!\sum_{\sum d_i=k}\frac{\prod(a_i-d_i)}{\prod d_i!}
=k!∑di=k∑∏di!∏(ai−di)
设后面这个东西的生成函数为
G
G
G,那么它其实就是
n
n
n 个形如
F
p
=
∑
i
=
0
∞
(
a
p
−
i
)
x
i
i
!
F_p=\sum_{i=0}^{\infty}\dfrac{(a_p-i)x^i}{i!}
Fp=∑i=0∞i!(ap−i)xi 的EGF卷起来。
然后把这个
F
F
F 化一下:
F
p
=
∑
i
=
0
∞
(
a
p
−
i
)
x
i
i
!
F_p=\sum_{i=0}^{\infty}\dfrac{(a_p-i)x^i}{i!}
Fp=i=0∑∞i!(ap−i)xi
=
a
p
⋅
∑
i
=
0
∞
x
i
i
!
−
∑
i
=
1
∞
x
i
(
i
−
1
)
!
=a_p\cdot\sum_{i=0}^{\infty}\dfrac{x^i}{i!}-\sum_{i=1}^{\infty}\dfrac{x^i}{(i-1)!}
=ap⋅i=0∑∞i!xi−i=1∑∞(i−1)!xi
=
a
p
⋅
∑
i
=
0
∞
x
i
i
!
−
∑
i
=
0
∞
x
i
+
1
i
!
=a_p\cdot\sum_{i=0}^{\infty}\dfrac{x^i}{i!}-\sum_{i=0}^{\infty}\dfrac{x^{i+1}}{i!}
=ap⋅i=0∑∞i!xi−i=0∑∞i!xi+1
=
(
a
p
−
x
)
⋅
∑
i
=
0
∞
x
i
i
!
=(a_p-x)\cdot\sum_{i=0}^{\infty}\dfrac{x^i}{i!}
=(ap−x)⋅i=0∑∞i!xi
=
(
a
p
−
x
)
⋅
e
x
=(a_p-x)\cdot e^x
=(ap−x)⋅ex
那么就有:
G
=
∏
p
=
1
n
F
p
G=\prod_{p=1}^nF_p
G=p=1∏nFp
G
=
∏
p
=
1
n
(
(
a
p
−
x
)
e
x
)
G=\prod_{p=1}^n((a_p-x)e^x)
G=p=1∏n((ap−x)ex)
G
=
(
∏
p
=
1
n
(
a
p
−
x
)
)
e
n
x
G=(\prod_{p=1}^n(a_p-x))e^{nx}
G=(p=1∏n(ap−x))enx
前面的
∏
p
=
1
n
(
a
p
−
x
)
\prod_{p=1}^n(a_p-x)
∏p=1n(ap−x) 可以暴力背包求解,设得到的函数为
f
f
f
那么就有:
G
=
(
∑
i
=
0
∞
f
i
x
i
)
×
(
∑
i
=
0
∞
(
n
x
)
i
i
!
)
G=(\sum_{i=0}^{\infty}f_ix^i)\times (\sum_{i=0}^{\infty}\frac{(nx)^i}{i!})
G=(i=0∑∞fixi)×(i=0∑∞i!(nx)i)
那么最终得到的期望就是
G
G
G 的第
k
k
k 项乘上
k
!
k!
k! 再除以
n
k
n^k
nk,即:
E
=
∑
i
=
0
f
i
⋅
k
i
‾
n
i
E=\sum_{i=0}\frac{f_i\cdot k^{\underline i}}{n^i}
E=i=0∑nifi⋅ki
答案就是:
∏
a
i
−
E
\prod a_i-E
∏ai−E
总复杂度
O
(
n
2
)
O(n^2)
O(n2)。
大图书馆(bibliotheca)
被A穿了的一道题…
15提交14AC一个96就离谱…
说实话我感觉这道题没有那么简单啊…
网络流显而易见,建图方法五花八门。
我的做法是转化为k重线段覆盖集问题然后直接做。
题解的做法把“不买书” 转化为一次买书和一次“卖书”,然后回溯连边,也是挺不错的。
子串选取 (substr)
挺可惜的一道题,差一点点。
感觉很经典的一道字符串题。
似乎必然是要SAM的,所以直接往那边想了。
然后又无脑的上了线段树合并 endpos 集合的套路。
然后找找性质二分搞吧搞吧就两个log了。
但是5e5两只log是过不去的…
先把串反过来,设计
d
p
i
dp_i
dpi 表示以
i
i
i 结尾的最大答案。
然后就有一个非常优秀的性质:
f
i
≤
f
i
−
1
+
1
f_i\le f_{i-1}+1
fi≤fi−1+1。
较为显然,把
f
i
f_i
fi 的一种方案每个串删去结尾,就能得到答案为
f
i
−
1
f_{i}-1
fi−1 且以
i
−
1
i-1
i−1 结尾的方案。
有了这个之后每次就不用二分 dp 值了,直接从上一个继承来然后不断暴力check,不合法就减减即可,有点类似于后缀数组求
h
e
i
g
h
t
height
height。
这样就把第二只 log 拿掉了,复杂度变为单 log,可以通过。
代码
T1
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define ok debug("OK\n")
inline ll read(){
ll x(0),f(1);char c=getchar();
while(!isdigit(c)){if(c=='-') f=-1;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
return x*f;
}
const int N=5e3+100;
const int B=150;
const int inf=2e9;
const int mod=1e9+7;
int n,m;
int a[N];
ll f[2][N],c[5];
inline ll ksm(ll x,ll k){
ll res(1);
while(k){
if(k&1) res=res*x%mod;
x=x*x%mod;
k>>=1;
}
return res;
}
signed main(){
freopen("calculate.in","r",stdin);
freopen("calculate.out","w",stdout);
//printf("%d\n",sizeof(t)/1024/1024);
n=read();m=read();
ll ans=1;
for(int i=1;i<=n;i++) a[i]=read(),ans=ans*a[i]%mod;
int now=1,pre=0;
f[now][0]=1;
for(int k=1;k<=n;k++){
c[0]=a[k];c[1]=mod-1;
swap(pre,now);
memset(f[now],0,sizeof(f[now]));
for(int i=0;i<=(k-1);i++){
for(int j=0;j<=1;j++){
f[now][i+j]=(f[now][i+j]+f[pre][i]*c[j])%mod;
}
}
}
ll E(0),bas=1,mi=1,ni=ksm(n,mod-2);
for(int i=0;i<=n;i++){
if(i) bas=bas*(m-i+1)%mod,mi=mi*ni%mod;
E=(E+f[now][i]*bas%mod*mi)%mod;
}
printf("%lld\n",(ans+mod-E)%mod);
return 0;
}
/*
12
abcdbabcabba
*/
T2
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define ok debug("OK\n")
inline ll read(){
ll x(0),f(1);char c=getchar();
while(!isdigit(c)){if(c=='-') f=-1;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
return x*f;
}
const int N=4e3+100;
const int B=150;
const int inf=2e9;
const int mod=998244353;
int n,m;
int ww=1e6+1;
int s,t,tot;
struct node{
int to,nxt,cap,w;
}p[N*N];
int fi[N],cur[N],cnt;
inline void addline(int x,int y,int c,int w){
p[++cnt]=(node){y,fi[x],c,w};fi[x]=cnt;
return;
}
inline void add(int x,int y,int c,int w){
addline(x,y,c,w);addline(y,x,0,-w);
//printf(" %d->%d cap=%d w=%d\n",x,y,c,w);
return;
}
int dis[N];
bool vis[N];
queue<int>q;
bool spfa(){
fill(dis,dis+1+tot,inf);
dis[s]=0;q.push(s);vis[s]=1;
while(!q.empty()){
int now=q.front();q.pop();
vis[now]=0;
for(int i=cur[now]=fi[now];~i;i=p[i].nxt){
int to=p[i].to;
if(!p[i].cap||dis[to]<=dis[now]+p[i].w) continue;
dis[to]=dis[now]+p[i].w;
if(!vis[to]){
vis[to]=1;q.push(to);
}
}
}
return dis[t]<inf;
}
int flow,cost;
int dfs(int x,int lim){
if(!lim||x==t){
cost+=lim*dis[t];
return lim;
}
if(vis[x]) return 0;
vis[x]=1;
int res(0);
for(int &i=cur[x];~i;i=p[i].nxt){
int to=p[i].to;
if(dis[to]!=dis[x]+p[i].w) continue;
int add=dfs(to,min(lim,p[i].cap));
res+=add;lim-=add;
p[i].cap-=add;p[i^1].cap+=add;
if(!lim) break;
}
if(!res) dis[x]=-1;
vis[x]=0;
return res;
}
void dinic(){
flow=cost=0;int tmp(0);
while(spfa()){
while((tmp=dfs(s,inf))) flow+=tmp;
}
return;
}
struct line{
int l,r,val;
}l[N];
int pre[N],lst[N];
int a[N],c[N],ans;
signed main(){
freopen("bibliotheca.in","r",stdin);
freopen("bibliotheca.out","w",stdout);
//printf("%d\n",sizeof(p)/1024/1024);
memset(fi,-1,sizeof(fi));cnt=-1;
n=read();m=read();
for(int i=1;i<=n;i++) a[i]=read();
for(int i=1;i<=n;i++) c[i]=read();
for(int i=1;i<=n;i++){
pre[i]=lst[a[i]];
ans+=c[a[i]];
lst[a[i]]=i;
}
for(int i=1;i<=n;i++){
int x=lst[i];
while(pre[x]){
l[++tot]=(line){pre[x],x,c[i]};
x=pre[x];
}
}
for(int i=1;i<=n;i++) l[++tot]=(line){i,i,ww};
ans+=n*ww;
int num=tot;
tot<<=1;s=++tot;t=++tot;int o=++tot;
add(s,o,m,0);
for(int i=1;i<=num;i++){
//printf("i=%d (%d %d)\n",i,l[i].l,l[i].r);
add(o,i,1,0);
add(i,i+num,1,-l[i].val);
add(i+num,t,1,0);
for(int j=1;j<=num;j++){
if(i==j) continue;
if(l[j].l>=l[i].r) add(i+num,j,1,0);
}
}
dinic();
printf("%d\n",ans+cost);
return 0;
}
/*
9 2
2 1 2 1 2 3 1 2 3
1 2 3 0 0 0 0 0 0
*/
T3
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define ok debug("OK\n")
inline ll read(){
ll x(0),f(1);char c=getchar();
while(!isdigit(c)){if(c=='-') f=-1;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
return x*f;
}
const int N=1e6+100;
const int B=150;
const int inf=2e9;
const int mod=998244353;
int n,m;
struct tree{
int ls,rs,sum,suf;
};
struct Segment_Tree{
#define mid ((l+r)>>1)
tree tr[N*30];
int tot;
inline int copy(int x){
tr[++tot]=tr[x];
return tot;
}
inline void pushup(int k){
tr[k].sum=tr[tr[k].ls].sum+tr[tr[k].rs].sum;
tr[k].suf=max(tr[tr[k].ls].suf,tr[tr[k].rs].suf);
return;
}
void upd(int &k,int l,int r,int p,int w){
if(!k) k=copy(0);
if(l==r){
tr[k].sum+=w;
tr[k].suf=l;
return;
}
if(p<=mid) upd(tr[k].ls,l,mid,p,w);
else upd(tr[k].rs,mid+1,r,p,w);
pushup(k);
}
int merge(int x,int y,int l,int r){
if(!x||!y) return x|y;
int now=copy(x);
tr[now].ls=merge(tr[now].ls,tr[y].ls,l,mid);
tr[now].rs=merge(tr[now].rs,tr[y].rs,mid+1,r);
pushup(now);
return now;
}
int findsuf(int k,int l,int r,int x,int y){
if(x>y) return 0;
if(!k) return 0;
if(x<=l&&r<=y) return tr[k].suf;
int res=0;
if(y>mid) res=max(res,findsuf(tr[k].rs,mid+1,r,x,y));
if(res) return res;
if(x<=mid) res=max(res,findsuf(tr[k].ls,l,mid,x,y));
return res;
}
#undef mid
}t;
int rt[N];
int fa[N],len[N],tr[N][26],tot=1,lst=1,state[N];
inline void ins(int c,int pos){
c-='a';
int cur=++tot,p=lst;lst=tot;
state[pos]=cur;
t.upd(rt[cur],1,n,pos,1);
len[cur]=len[p]+1;
for(;p&&!tr[p][c];p=fa[p]) tr[p][c]=cur;
if(!tr[p][c]) fa[cur]=1;
else{
int q=tr[p][c];
if(len[q]==len[p]+1) fa[cur]=q;
else{
int nq=++tot;
len[nq]=len[p]+1;fa[nq]=fa[q];
for(int i=0;i<26;i++) tr[nq][i]=tr[q][i];
fa[cur]=fa[q]=nq;
for(;p&&tr[p][c]==q;p=fa[p]) tr[p][c]=nq;
}
}
return;
}
vector<int>v[N];
int pl[N][20];
void dfs(int x){
pl[x][0]=fa[x];
//printf("i=%d k=0 pl=%d\n",i,pl[i][0]);
for(int k=1;pl[x][k-1];k++){
pl[x][k]=pl[pl[x][k-1]][k-1];
//printf("i=%d k=%d mid=%d pl=%d\n",i,k,pl[i][k-1],pl[i][k]);
}
for(int to:v[x]){
dfs(to);
rt[x]=t.merge(rt[x],rt[to],1,n);
}
return;
}
void build(){
for(int i=2;i<=tot;i++) v[fa[i]].push_back(i);
dfs(1);
return;
}
inline int jump(int x,int l){
//printf(" jump: x=%d L=%d\n",x,l);
for(int k=19;k>=0;k--){
//if(pl[x][k]) printf(" k=%d pl=%d len=%d\n",k,pl[x][k],len[pl[x][k]]);
if(len[pl[x][k]]>=l) x=pl[x][k];
}
return x;
}
char s[N],ss[N];
int dp[N],mx,ans;
bool check(int x,int L){
//printf("check: x=%d L=%d\n",x,L);
int s=jump(state[x],L-1);
int pl=t.findsuf(rt[s],1,n,1,x-L);
//printf(" cutpre: s=%d (%d %d) pl=%d state=%d\n",s,1,x-L,pl,state[x]);
if(dp[pl]>=L-1) return true;
if(x>1){
s=jump(state[x-1],L-1);
pl=t.findsuf(rt[s],1,n,1,x-L);
//printf(" cutsuf: s=%d (%d %d) pl=%d state=%d\n",s,1,x-L,pl,state[x-1]);
if(dp[pl]>=L-1) return true;
}
return false;
}
void DP(){
for(int i=1;i<=n;i++){
dp[i]=dp[i-1]+1;
while(dp[i]>1&&!check(i,dp[i])) --dp[i];
ans=max(ans,dp[i]);
//printf("i=%d dp=%d\n\n",i,dp[i]);
}
return;
}
signed main(){
freopen("substr.in","r",stdin);
freopen("substr.out","w",stdout);
//printf("%d\n",sizeof(t)/1024/1024);
n=read();
scanf(" %s",ss+1);
for(int i=1;i<=n;i++) s[i]=ss[n-i+1];
//printf("%s\n",s+1);
for(int i=1;i<=n;i++) ins(s[i],i);
//for(int i=1;i<=tot;i++) printf("i=%d fa=%d len=%d\n",i,fa[i],len[i]);
build();
DP();
printf("%d\n",ans);
return 0;
}
/*
12
abcdbabcabba
*/
总结
感觉最近不再像一开始那样疯狂挂分了。
明天就要上学校了,加油!awa