2.10模拟总结

前言

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' aiai
前面已知,考虑如何算后面的贡献。

设每个数的删除次数为 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=kdi!k!(aidi)
= 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=kdi!(aidi)
设后面这个东西的生成函数为 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=0i!(api)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=0i!(api)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)!} =api=0i!xii=1(i1)!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!} =api=0i!xii=0i!xi+1
= ( a p − x ) ⋅ ∑ i = 0 ∞ x i i ! =(a_p-x)\cdot\sum_{i=0}^{\infty}\dfrac{x^i}{i!} =(apx)i=0i!xi
= ( a p − x ) ⋅ e x =(a_p-x)\cdot e^x =(apx)ex
那么就有:
G = ∏ p = 1 n F p G=\prod_{p=1}^nF_p G=p=1nFp
G = ∏ p = 1 n ( ( a p − x ) e x ) G=\prod_{p=1}^n((a_p-x)e^x) G=p=1n((apx)ex)
G = ( ∏ p = 1 n ( a p − x ) ) e n x G=(\prod_{p=1}^n(a_p-x))e^{nx} G=(p=1n(apx))enx
前面的 ∏ p = 1 n ( a p − x ) \prod_{p=1}^n(a_p-x) p=1n(apx) 可以暴力背包求解,设得到的函数为 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=0fixi)×(i=0i!(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=0nifiki
答案就是:
∏ a i − E \prod a_i-E aiE
总复杂度 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 fifi1+1
较为显然,把 f i f_i fi 的一种方案每个串删去结尾,就能得到答案为 f i − 1 f_{i}-1 fi1 且以 i − 1 i-1 i1 结尾的方案。
有了这个之后每次就不用二分 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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值