文章目录
- 前言
- 题目解析
- 随机减法(calculate)
- 大图书馆(bibliotheca)
- 子串选取 (substr)
- 代码
- T1
- T2
- T3
- 总结
前言
200pts
40+100+60
rnk3
拿到牌勒嘿嘿嘿(脑补流口水黄豆)
T3两个log想在ybt的机子上过5e5确实是奢望了。
把串反过来改一改dp定义看出题解的那个性质就可以拿掉那个log,有些可惜。
但毕竟没有挂分,还是不错的=v=
题目解析
随机减法(calculate)
乍一看这个题就感觉在 OI-wiki 上见过。
但是就记得在OIwiki上有了,属于那个章节,怎么做统统不记得…
于是就只配打暴力了…
其实也看出了卷积,但那个东西需要再变成封闭形式化一下,必然还是脱离不了 O(k)O(k)O(k) 的复杂度。
而且看到 1e9+71e9+71e9+7 这种模数本能的觉得不是多项式…
而且为啥我的dp转移和题解又不一样,难看的很难化了…
题目的贡献其实也就是 ∏ai\prod a_i∏ai 的变化量。
即:
∏ai−∏ai′\prod a_i-\prod a_i'∏ai−∏ai′
前面已知,考虑如何算后面的贡献。
设每个数的删除次数为 d1...nd_{1...n}d1...n,则有:
ans=∑∑di=kk!∏(ai−di)∏di!ans=\sum_{\sum d_i=k}\frac{k!\prod(a_i-d_i)}{\prod d_i!}ans=∑di=k∑∏di!k!∏(ai−di)
=k!∑∑di=k∏(ai−di)∏di!=k!\sum_{\sum d_i=k}\frac{\prod(a_i-d_i)}{\prod d_i!}=k!∑di=k∑∏di!∏(ai−di)
设后面这个东西的生成函数为 GGG,那么它其实就是 nnn 个形如 Fp=∑i=0∞(ap−i)xii!F_p=\sum_{i=0}^{\infty}\dfrac{(a_p-i)x^i}{i!}Fp=∑i=0∞i!(ap−i)xi 的EGF卷起来。
然后把这个 FFF 化一下:
Fp=∑i=0∞(ap−i)xii!F_p=\sum_{i=0}^{\infty}\dfrac{(a_p-i)x^i}{i!}Fp=i=0∑∞i!(ap−i)xi
=ap⋅∑i=0∞xii!−∑i=1∞xi(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
=ap⋅∑i=0∞xii!−∑i=0∞xi+1i!=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
=(ap−x)⋅∑i=0∞xii!=(a_p-x)\cdot\sum_{i=0}^{\infty}\dfrac{x^i}{i!}=(ap−x)⋅i=0∑∞i!xi
=(ap−x)⋅ex=(a_p-x)\cdot e^x=(ap−x)⋅ex
那么就有:
G=∏p=1nFpG=\prod_{p=1}^nF_pG=p=1∏nFp
G=∏p=1n((ap−x)ex)G=\prod_{p=1}^n((a_p-x)e^x)G=p=1∏n((ap−x)ex)
G=(∏p=1n(ap−x))enxG=(\prod_{p=1}^n(a_p-x))e^{nx}G=(p=1∏n(ap−x))enx
前面的 ∏p=1n(ap−x)\prod_{p=1}^n(a_p-x)∏p=1n(ap−x) 可以暴力背包求解,设得到的函数为 fff
那么就有:
G=(∑i=0∞fixi)×(∑i=0∞(nx)ii!)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)
那么最终得到的期望就是 GGG 的第 kkk 项乘上 k!k!k! 再除以 nkn^knk,即:
E=∑i=0fi⋅ki‾niE=\sum_{i=0}\frac{f_i\cdot k^{\underline i}}{n^i}E=i=0∑nifi⋅ki
答案就是:
∏ai−E\prod a_i-E∏ai−E
总复杂度 O(n2)O(n^2)O(n2)。
大图书馆(bibliotheca)
被A穿了的一道题…
15提交14AC一个96就离谱…
说实话我感觉这道题没有那么简单啊…
网络流显而易见,建图方法五花八门。
我的做法是转化为k重线段覆盖集问题然后直接做。
题解的做法把“不买书” 转化为一次买书和一次“卖书”,然后回溯连边,也是挺不错的。
子串选取 (substr)
挺可惜的一道题,差一点点。
感觉很经典的一道字符串题。
似乎必然是要SAM的,所以直接往那边想了。
然后又无脑的上了线段树合并 endpos 集合的套路。
然后找找性质二分搞吧搞吧就两个log了。
但是5e5两只log是过不去的…
先把串反过来,设计 dpidp_idpi 表示以 iii 结尾的最大答案。
然后就有一个非常优秀的性质:fi≤fi−1+1f_i\le f_{i-1}+1fi≤fi−1+1。
较为显然,把 fif_ifi 的一种方案每个串删去结尾,就能得到答案为 fi−1f_{i}-1fi−1 且以 i−1i-1i−1 结尾的方案。
有了这个之后每次就不用二分 dp 值了,直接从上一个继承来然后不断暴力check,不合法就减减即可,有点类似于后缀数组求 heightheightheight。
这样就把第二只 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