回文树裸题
利用lazy思想延迟标记,最后所有标记倒序更新即可
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define per(i,r,l) for(int i=r;i>=l;i--)
typedef long long ll;
const int N=300000+5;
int len[N],suf[N],ch[N][26],sum[N];
int node,last;
void init(){
len[suf[suf[2]=1]=1]=-1;
node=last=2;
}
char s[N];
bool add(int i){
int cur=last,c=s[i]-'a';
while(s[i-len[cur]-1]!=s[i])cur=suf[cur];
bool flag=true;
if(!ch[cur][c]){
len[last=++node]=len[cur]+2;ch[cur][c]=last;
int tmp=suf[cur];
while(s[i-len[tmp]-1]!=s[i])tmp=suf[tmp];
suf[last]=len[last]==1?2:ch[tmp][c];
}else flag=false;
last=ch[cur][c];sum[last]++;
return flag;
}
int main(){
//freopen("a.in","r",stdin);
scanf("%s",s+1);int n=strlen(s+1);
init();
rep(i,1,n)add(i);
per(i,node,1)sum[suf[i]]+=sum[i];
ll ans=0;
rep(i,1,node)ans=max(ans,(ll)len[i]*sum[i]);
printf("%lld\n",ans);
return 0;
}
不会证明的DP系列。。。。。
打个表发现切割顺序不影响答案,于是斜率DP就好了
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define per(i,r,l) for(int i=r;i>=l;i--)
#define mmt(a,v) memset(a,v,sizeof(a))
typedef long long ll;
const int N=100000+5;
ll f[205][N],s[N];
int n,k,last;
int q[N],h,t;
double slop(int a,int b){
return double(f[last][a]-f[last][b])/double(s[a]-s[b]);
}
void print(int i,int j){
if(i==1)return;
per(k,j-1,0)
if(f[i][j]==f[i-1][k]+(s[j]-s[k])*(s[n]-s[j])){
print(i-1,k);
printf("%d ",k);
return;
}
}
int main(){
//freopen("a.in","r",stdin);
scanf("%d%d",&n,&k);
rep(i,1,n)scanf("%lld",&s[i]),s[i]+=s[i-1];
rep(i,1,n)f[1][i]=s[i]*(s[n]-s[i]);
rep(i,2,k+1){
h=0;t=-1;last=i-1;
rep(j,1,i-1){
while(h<t&&s[q[t-1]]!=s[q[t]]&&(s[q[t]]==s[j]||slop(q[t-1],q[t])<slop(q[t],j)))t--;
q[++t]=j;
}
rep(j,i,n){
while(h<t&&(s[q[h+1]]==s[q[h]]||slop(q[h+1],q[h])>s[n]-s[j]))h++;
f[i][j]=f[i-1][q[h]]+(s[j]-s[q[h]])*(s[n]-s[j]);
while(h<t&&s[q[t-1]]!=s[q[t]]&&(s[q[t]]==s[j]||slop(q[t-1],q[t])<slop(q[t],j)))t--;
q[++t]=j;
}
}
printf("%lld\n",f[k+1][n]);
print(k+1,n);putchar('\n');
return 0;
}
树形DP一遍求出每个点为根的子树的答案,然后维护最大值和次大值进行换根DP,O(n)。
由于我比较懒,直接上堆了,O(nlogn)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int inf=1e9;
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define per(i,r,l) for(int i=r;i>=l;i--)
#define mmt(a,v) memset(a,v,sizeof(a))
#define tra(i,u) for(int i=head[u];i;i=e[i].next)
const int N=200000+5;
struct Edge{int to,next,v;}e[N<<1];
int head[N],cnt;
void ins(int u,int v,int w){e[++cnt]=(Edge){v,head[u],w};head[u]=cnt;}
int f[N],g[N];
struct Twoheap{
priority_queue<int>heap,del;
Twoheap(){heap.push(-inf);}
void push(int x){heap.push(x);}
void erase(int x){del.push(x);}
int top(){while(!del.empty()&&del.top()==heap.top())del.pop(),heap.pop();return heap.top();}
}s[N];
void dp(int u,int fa,int c){
f[u]=g[u]=0;
tra(i,u){
int v=e[i].to;if(v==fa)continue;
dp(v,u,e[i].v);
f[u]+=max(g[v],f[v]);
s[u].push(-max(g[v],f[v])+f[v]+e[i].v);
}
g[u]=s[u].top()+f[u]+c;
}
void dp2(int u,int fa){
tra(i,u){
int v=e[i].to;if(v==fa)continue;
s[u].erase(f[v]-max(g[v],f[v])+e[i].v);
s[v].push(e[i].v-max(0,s[u].top()+e[i].v));
f[v]+=f[u]-max(f[v],g[v])+max(0,s[u].top()+e[i].v);
dp2(v,u);
}
}
int main(){
//freopen("a.in","r",stdin);
int n;scanf("%d",&n);
rep(i,2,n){int u,v,w;scanf("%d%d%d",&u,&v,&w);ins(u,v,w);ins(v,u,w);}
dp(1,-1,0);dp2(1,-1);
int ans=0;
rep(i,1,n)ans=max(ans,f[i]);
printf("%d\n",ans);
return 0;
}