Solution Set - APIO2014

发布时间 2023-04-04 21:07:32作者: by_chance

目录


A 回文串

给定字符串 \(S\)。对 \(S\) 的所有回文子串,求其长度与出现次数之积的最大值。

\(|S| \le 300000\)


点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=3e5+5;
int n,m,a[N<<1],lg2[N],cnt,str[N<<2][2];
int S,x[N],y[N],c[N],sa[N],rk[N],h[N],mn[N][25];
ll ans;char s[N],t[N<<1];
void SA(){
	S=300;
	for(int i=1;i<=n;i++){x[i]=s[i];++c[x[i]];}
	for(int i=2;i<=S;i++)c[i]+=c[i-1];
	for(int i=n;i>=1;i--)sa[c[x[i]]--]=i;
	for(int k=1;k<=n;k<<=1){
		int num=0;
		for(int i=n-k+1;i<=n;i++)y[++num]=i;
		for(int i=1;i<=n;i++)if(sa[i]>k)y[++num]=sa[i]-k;
		for(int i=1;i<=S;i++)c[i]=0;
		for(int i=1;i<=n;i++)c[x[i]]++;
		for(int i=2;i<=S;i++)c[i]+=c[i-1];
		for(int i=n;i>=1;i--){sa[c[x[y[i]]]--]=y[i];y[i]=0;}
		for(int i=1;i<=n;i++)swap(x[i],y[i]);
		num=1;x[sa[1]]=1;
		for(int i=2;i<=n;i++){
			if(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])
				x[sa[i]]=num;
			else x[sa[i]]=++num;
		}
		if(num==n)break;S=num;
	}
}
void LCP(){
	int k=0;
	for(int i=1;i<=n;i++)rk[sa[i]]=i;
	for(int i=1;i<=n;i++){
		if(rk[i]==1)continue;
		if(k)k--;
		int j=sa[rk[i]-1];
		while(i+k<=n&&j+k<=n&&s[i+k]==s[j+k])k++;
		h[rk[i]]=k;
	}
}
void build_ST(){
	for(int i=1;i<=n;i++)mn[i][0]=h[i];
	for(int j=1;(1<<j)<=n;j++)
		for(int i=1;i<=n-(1<<j)+1;i++)
			mn[i][j]=min(mn[i][j-1],mn[i+(1<<j-1)][j-1]);
}
int query(int l,int r){
	if(l>r)return 0;
	int i=lg2[r-l+1];int j=(1<<i);
	return min(mn[l][i],mn[r-j+1][i]);
}
void Manacher(){
	int l=1,r=0;
	for(int i=1;i<=m;++i){
		if(r>=i&&i+a[l+r-i]<=r)a[i]=a[l+r-i];
		else{
			int k=max(0,r-i);
			while(i+k<=m&&i-k>=1&&t[i+k]==t[i-k]){
				++k;++cnt;
				str[cnt][0]=(i-k+2)/2;str[cnt][1]=(i+k-1)/2;
				if(str[cnt][0]>str[cnt][1])--cnt;
			}
			a[i]=k;l=i-k+1;r=i+k-1;
		}
	}
}
ll solve(int l,int r){
	int L=0,R=0,p1=0,p2=0;
	L=2;R=rk[l];p1=rk[l]+1;
	while(L<=R){
		int mid=L+R>>1;
		if(query(mid,rk[l])>=r-l+1)p1=mid,R=mid-1;
		else L=mid+1;
	}
	L=rk[l]+1;R=n;p2=rk[l];
	while(L<=R){
		int mid=L+R>>1;
		if(query(rk[l]+1,mid)>=r-l+1)p2=mid,L=mid+1;
		else R=mid-1;
	}
	return 1ll*(p2-p1+2)*(r-l+1);
}
int main(){
	scanf("%s",s+1);
	n=strlen(s+1);m=2*n+1;t[1]='#';
	for(int i=0;(1<<i)<=n;i++)lg2[1<<i]=i;
	for(int i=1,lst=0;i<=n;i++){
		if(lg2[i])lst=lg2[i];
		else lg2[i]=lst;
	}
	for(int i=n;i>=1;--i)t[2*i]=s[i],t[2*i+1]='#';
	Manacher();SA();LCP();build_ST();
	for(int i=1;i<=cnt;i++)ans=max(ans,solve(str[i][0],str[i][1]));
	printf("%lld\n",ans);
	return 0;
}

B 序列分割

将长为 \(n\) 的数组分 \(k\) 次,每次在某一段内部将其分成两段,得分是分出两段的元素和的乘积。\(k\) 次操作的总得分是每次得分之和。求最大得分并输出方案。

\(n \le 100000\)\(k \le 200\)\(k \lt n\)


点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5,K=205;
int n,k,lst[K][N];
ll dp[K][N],s[N],b[N],sum;
int q[N],he,ta;
int main(){
	scanf("%d%d",&n,&k);
	for(int i=1,x;i<=n;i++){
		scanf("%d",&x);
		s[i]=s[i-1]+x;sum+=x;
	}
	memset(dp,0x3f,sizeof(dp));
	for(int j=1;j<=n;j++)dp[1][j]=s[j]*s[j];
	for(int i=2;i<=k+1;i++){
		he=ta=1;q[1]=i-1;
		b[i-1]=s[i-1]*s[i-1]+dp[i-1][i-1];
		for(int j=i;j<=n;j++){
			while(he<ta&&(b[q[he+1]]-b[q[he]])<=2*s[j]*(s[q[he+1]]-s[q[he]]))++he;
			dp[i][j]=s[j]*s[j]-2*s[q[he]]*s[j]+b[q[he]];lst[i][j]=q[he];
			b[j]=s[j]*s[j]+dp[i-1][j];
			while(ta>he&&(b[j]-b[q[ta-1]])*(s[j]-s[q[ta]])>=
						 (b[j]-b[q[ta]])*(s[j]-s[q[ta-1]]))--ta;
			q[++ta]=j;
		}
	}
	printf("%lld\n",(sum*sum-dp[k+1][n])/2);
	for(int i=k,j=lst[k+1][n];i>=1;i--){
		printf("%d ",j);
		j=lst[i][j];
	}
	return 0;
}

C 连珠线

初始有一个点,用如下两种操作生成一棵树:

Append(w, v):一个新的点 \(w\) 和一个已经添加的点 \(v\) 用红边连接起来。

Insert(w, u, v):一个新的点 \(w\) 插入到用红边连起来的两个点 \(u, v\) 之间。具体过程是删去 \(u, v\) 之间红边,分别用蓝边连接 \(u, w\)\(w, v\)

给定 \(n\) 个点的最终状态。求蓝边权值之和的最大可能值。

\(n \le 200000\)


点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5,INF=1<<29;
int n,dp[N][2],g[N][2],ans;
int head[N],nxt[N<<1],ver[N<<1],val[N<<1],tot;
int max(int a,int b){return a>b?a:b;}
void add(int u,int v,int w){
	ver[++tot]=v;
	val[tot]=w;
	nxt[tot]=head[u];
	head[u]=tot;
}
void dfs(int u,int fa){
	dp[u][0]=0;g[u][0]=g[u][1]=-INF;
	for(int i=head[u];i;i=nxt[i]){
		int v=ver[i];
		if(v==fa)continue;
		dfs(v,u);
		int tmp=max(dp[v][0],dp[v][1]+val[i]);
		dp[u][0]+=tmp;
		tmp=dp[v][0]+val[i]-tmp;
		if(tmp>g[u][0])g[u][1]=g[u][0],g[u][0]=tmp;
		else if(tmp>g[u][1])g[u][1]=tmp;
	}
	dp[u][1]=g[u][0]+dp[u][0];
}
void redfs(int u,int fa,int in){
	for(int i=head[u];i;i=nxt[i]){
		int v=ver[i];
		if(v==fa)continue;
		int t0=dp[u][0],t1=dp[u][1],t2=dp[v][0],t3=dp[v][1];
		int tmp=max(dp[v][0],dp[v][1]+val[i]);
		dp[u][0]=dp[u][0]-tmp;
		if(g[u][0]==val[i]+dp[v][0]-tmp)dp[u][1]=dp[u][0]+max(in,g[u][1]);
		else dp[u][1]=dp[u][0]+max(in,g[u][0]);
		tmp=max(dp[u][0],dp[u][1]+val[i]);
		dp[v][0]=dp[v][0]+tmp;
		dp[v][1]=dp[v][0]+max(g[v][0],val[i]+dp[u][0]-tmp);
		ans=max(ans,dp[v][0]);
		redfs(v,u,val[i]+dp[u][0]-tmp);
		dp[u][0]=t0;dp[u][1]=t1;dp[v][0]=t2;dp[v][1]=t3;
	}
}
int main(){
	scanf("%d",&n);
	for(int i=1,u,v,w;i<n;i++){
		scanf("%d%d%d",&u,&v,&w);
		add(u,v,w);add(v,u,w);
	}
	dfs(1,-1);ans=dp[1][0];
	redfs(1,-1,-INF);
	printf("%d\n",ans);
	return 0;
}