Wannafly挑战赛29题解
这套题目非常有意思啊23333……话说为啥没有上条先生的呢……
\(A\) 御坂美琴
蠢了……首先先判总共加起来等不等于\(n\),不是的话就不行
然后dfs记录\(n\)不断分下去能分成哪些数,用map记录一下,判断是否所有数都能被分出来就是了
//minamoto
#include<bits/stdc++.h>
#define int long long
#define fp(i,a,b) for(register int i=a,I=b+1;i<I;++i)
#define fd(i,a,b) for(register int i=a,I=b-1;i>I;--i)
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
int read(){
int res,f=1;char ch;
while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
return res*f;
}
const int N=1e5+5;
int n,sum,x,m,a[N];map<int,bool>mp;
void dfs(int n){
if(mp[n])return;mp[n]=1;
dfs(n/2),dfs(n-n/2);
}
signed main(){
// freopen("testdata.in","r",stdin);
n=read(),m=read();
fp(i,1,m)a[i]=read(),sum+=a[i];
if(sum!=n)return puts("ham"),0;
dfs(n);fp(i,1,m)if(!mp[a[i]])return puts("ham"),0;
return puts("misaka"),0;
}
\(B\) 白井黑子
好坑啊……话说居然有\(f(0)=1\)……
如果\(k=0\),那么显然只有\(f(a)=1\)且\(f(b)=1\)的时候是\(gg\)的,总的方案数减去这种方案数就行了
对于\(k>0\)的情况,显然所有位上有\(0\)的都不用管了(\(0\)除外)。
首先\(f(a)\times f(b)\)最多只有\(2,3,5,7\)四个质因子,那么如果\(ab\)不合法当且仅当\(f(a)\times f(b)\)里四个质因子的出现次数模\(k\)都为\(0\),把每个\(f(a)\)的四个质因子的出现次数哈希一下,并顺便算一下不合法的\(b\)的哈希值,用之前所有的减去不合法的就行了。开个\(map\)即可
所以我真的不知道为啥我死都用不了\(unordered\_map\)……
//minamoto
#include<bits/stdc++.h>
#define R register
#define ll long long
#define pi pair<int,int>
#define fi first
#define se second
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
ll read(){
R ll res,f=1;R char ch;
while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
return res*f;
}
int n,k,cnt;ll res,x;
inline bool ck0(R ll x){while(x){if(x%10==0)return true;x/=10;}return false;}
inline bool ck1(R ll x){while(x){if(x%10!=1)return false;x/=10;}return true;}
void qwq(){
fp(i,1,n)x=read(),cnt+=ck1(x);
printf("%lld\n",(1ll*n*(n-1)>>1)-(1ll*cnt*(cnt-1)>>1));
}
const int P1=998244353,P2=1e9+7;
const int Base1=233,Base2=19260817;
pi bin[5],now,las;map<pi,int>mp;
inline pi operator +(const pi &a,const pi &b){return pi((a.fi+b.fi)%P1,(a.se+b.se)%P2);}
inline pi operator *(const int &a,const pi &b){return pi(1ll*a*b.fi%P1,1ll*a*b.se%P2);}
int c[15];
int main(){
// freopen("testdata.in","r",stdin);
n=read(),k=read();
if(!k)return qwq(),0;
bin[0].fi=Base1,bin[0].se=Base2;
fp(i,1,5)bin[i].fi=1ll*bin[i-1].fi*Base1%P1,bin[i].se=1ll*bin[i-1].se*Base2%P2;
fp(i,1,n){
x=read();if(ck0(x))continue;
fp(j,0,3)c[j]=0;
while(x){
switch(x%10){
case 0:break;
case 1:break;
case 2:++c[0];break;
case 3:++c[1];break;
case 4:++c[0],++c[0];break;
case 5:++c[2];break;
case 6:++c[0],++c[1];break;
case 7:++c[3];break;
case 8:c[0]+=3;break;
case 9:++c[1],++c[1];break;
}
x/=10;
}
now.fi=now.se=las.fi=las.se=0;
fp(j,0,3)now=now+c[j]%k*bin[j],las=las+(k-c[j]%k)%k*bin[j];
res+=cnt-mp[las],++mp[now],++cnt;
}
printf("%lld\n",res);
return 0;
}
\(C\) 左方之地
根据期望的线性,我们转化为求每个节点的深度的期望
因为每个子树中的节点编号都小于自己的标号,那么我们考虑这样一种构造法:钦定第一个点为根节点,之后再把第二个点挂上去,再挂第三个点……容易发现这样一定满足子树中所有节点标号小于自己。因为总的排列个数有\(n!\)种,这样构造出的树也总共有\(n!\)种,所以我们的排列和二叉树就是一一对应的了
因为\(i\)号节点的深度和它后面所有节点都没有关系,那么我们就一个一个来考虑了
设\(f_i\),表示“所有\(i\)个节点的树中,所有能挂叶子的位置,它们的父亲的深度的和”
这个可能比较难理解,以样例那棵树为例,\(1\)下面可以挂一个叶子,\(2\)下面可以挂\(1\)个叶子,\(3\)下面可以挂\(2\)个叶子,那么这棵树的权值就是\(1\times 1+1\times 2+2\times 3=9\),而\(f_i\)就是所有\(i\)个节点的树的权值之和
考虑一下这东西怎么转移,先把柿子给出来
\]
首先\(i-1\)个节点共有\(i!\)棵树,第\(i\)个节点在每棵树上有\(i\)个位置可以挂,所以每棵树会被挂到\(i\)次,那么不考虑对深度的影响的话就是\(if_{i-1}\)。然而挂上去的那个位置会少掉,那么要减去一个\(f_{i-1}\),而新挂上的叶子的深度是原来节点的深度\(+1\),而且新挂上去的节点会提供两个可以挂叶子的位置,总共挂了\(i!\)次,所以要加上后面的东西
所以这东西和期望深度有半毛钱关系么……
我们发现后面那个东西似乎很有用诶……“新挂上的叶子的深度是原来节点的深度\(+1\)”……那么\(i\)挂上去的所有情况的深度总数就是\(f_{i-1}+i!\),而树的情况总共有\(i!\)种,那么期望深度就是\({f_i+i!\over i!}\)
然后直接递推就可以了
//minamoto
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
int read(){
R int res,f=1;R char ch;
while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
return res*f;
}
char sr[1<<21],z[20];int C=-1,Z=0;
inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
void print(R int x){
if(C>1<<20)Ot();if(x<0)sr[++C]='-',x=-x;
while(z[++Z]=x%10+48,x/=10);
while(sr[++C]=z[Z],--Z);sr[++C]='\n';
}
const int N=1e5+5,P=998244353;
inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;}
inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;}
inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
int ksm(R int x,R int y){
R int res=1;
for(;y;y>>=1,x=mul(x,x))(y&1)?res=mul(res,x):0;
return res;
}
int f[N],g[N],fac[N],ifac[N],a[N],n,res;
int main(){
// freopen("testdata.in","r",stdin);
n=read(),fac[0]=ifac[0]=1;
fp(i,1,n)a[i]=read(),fac[i]=mul(fac[i-1],i);
ifac[n]=ksm(fac[n],P-2);fd(i,n-1,1)ifac[i]=mul(ifac[i+1],i+1);
fp(i,1,n)f[i]=(1ll*(i+1)*f[i-1]+2ll*fac[i])%P,g[i]=1ll*(f[i-1]+fac[i])*ifac[i]%P;
fp(i,1,n)res=add(res,mul(g[i],a[i]));
printf("%d\n",res);
return 0;
}
\(D\) 风斩冰华
所以\(dp\)是咱的硬伤啊……
我们先把它转化成一棵无根树,那么影响每个节点度数的可以分为儿子和父亲的情况来考虑,父亲的情况还要考虑是先于父亲删还是后于父亲删
设\(dp_{u,0/1/2}\)分别表示不删\(/\)先于父亲删\(/\)后于父亲删的最大权值
首先\(dp_{u,0}\)很好转移
\]
然而\(dp_{u,1}\)和\(dp_{u,2}\)的转移就显得有些辣手了
我们先考虑每个儿子的贡献,如果这个儿子保留,贡献就是\(\max(dp_{v,0},dp_{v,2})\),如果删掉,贡献就是\(dp_{v,1}\)
那么我们一开始强制所有节点都删掉(有可能有的\(dp_{v,1}\)不合法,这种情况要除去),并计算贡献。那么如果要把这个节点保留下来,贡献就要加上\(\max(dp_{v,0},dp_{v,2})-dp_{v,1}\)
我们把所有的\(\max(dp_{v,0},dp_{v,2})-dp_{v,1}\)从大到小排个序,先加点加到满足度数\(\geq l\),然后在满足度数限制的情况下把剩下能加的点都加进去就行了(具体细节可以看代码)
//minamoto
#include<bits/stdc++.h>
#define R register
#define ll long long
#define inf 1e18
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
int read(){
R int res,f=1;R char ch;
while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
return res*f;
}
const int N=5e5+5;
struct eg{int v,nx;}e[N<<1];int head[N],tot;
inline void Add(R int u,R int v){e[++tot]={v,head[u]},head[u]=tot;}
ll dp[N][3],st[N];int n,l,r,a[N];
inline bool cmp(const int &x,const int &y){return x>y;}
void dfs(int u,int fa){
dp[u][0]=0,dp[u][1]=dp[u][2]=a[u];
go(u)if(v!=fa)dfs(v,u),dp[u][0]+=max(dp[v][0],dp[v][1]);
int top=0,tot=0;
go(u)if(v!=fa){
ll tmp=(dp[v][1]==-inf)?(++tot,max(dp[v][0],dp[v][2])):(st[++top]=max(dp[v][0],dp[v][2])-dp[v][1],dp[v][1]);
dp[u][1]+=tmp,dp[u][2]+=tmp;
// printf("%d %d %d\n",u,v,tmp);
}
// printf("%d %lld %lld %lld\n",u,dp[u][0],dp[u][1],dp[u][2]);
sort(st+1,st+1+top,cmp);
int h=tot+1,t;
if(h>r)dp[u][1]=-inf;
else{
for(t=1;t<=top&&h+t<=l;++t)dp[u][1]+=st[t];
if(h+t-1<l)dp[u][1]=-inf;
else{
for(;t<=top&&h+t<=r;++t){
if(st[t]<=0)break;
dp[u][1]+=st[t];
}
}
}
h=tot;
if(h>r)dp[u][2]=-inf;
else{
for(t=1;t<=top&&h+t<=l;++t)dp[u][2]+=st[t];
if(h+t-1<l)dp[u][2]=-inf;
else{
for(;t<=top&&h+t<=r;++t){
if(st[t]<=0)break;
dp[u][2]+=st[t];
}
}
}
}
int main(){
// freopen("testdata.in","r",stdin);
n=read(),l=read(),r=read();
fp(i,1,n)a[i]=read();
for(R int i=1,u,v;i<n;++i)u=read(),v=read(),Add(u,v),Add(v,u);
dfs(1,0);
printf("%lld\n",max(dp[1][0],dp[1][2]));
return 0;
}
\(E\) 一方通行
没有题解的代码食用体验极差……
我们可以把答案拆成若干条路径(这里的路径需要至少有两个节点),相邻两条路径之间通过一条跨越树的边相连,即每一段都是某棵树上的一条路径。这些路径需要两两不交,并且相邻两端不在同一棵树上。我们需要先计算选取路径的方案数,再计算它们排列的方案数
先考虑一棵树,我们设\(f_{u,i,j,0/1/2}\)记录方案数,表示\(u\)所在的子树,从中选出\(j\)条路径,这些路径上总共有\(i\)个点,\(0\)表示\(u\)不在路径中,\(1\)表示\(u\)是路径的端点且这条路径还需要延伸,\(2\)表示\(u\)所在的路径已经全部选完了。转移的话……太长了看代码吧……
那么整棵树的方案就是\(f_{1,i,j,0}+f_{1,i,j,2}\),记为\(sum_{T,i,j}\),\(T\)表示这是第几棵树
然后我们需要把它们给排列起来,枚举一下三棵树上选择的路径条数\(x,y,z\),可以转化成这样一个问题:有三种颜色的物品,每种颜色的物品分别有\(x,y,z\),求没有两种相同颜色相邻的排列数
设\(g_{i,j,k,op}\)表示三种颜色的物品分别有\(i,j,k\)个,且最后一个颜色为\(op\)的排列个数,转移的话,枚举一下新加的物品是什么颜色就行了,如果把三种颜色分别记为\(0,1,2\)的话,顺便可以记一个\(g_{i,j,k,3}\)表示\(g_{i,j,k,0}+g_{i,j,k,1}+g_{i,j,k,2}\)
接下来就是统计答案的时间~~~
枚举三棵树分别选了\(x,y,z\)个点,枚举三棵树上分别有\(i,j,l\)条路径,那么答案就要加上
\]
前面三个\(sum\)好理解,\(g_{i,j,l,3}\)就是合法排列个数,然后我们数的路径都是无向的所以要给每条路径定向,乘上个\(2^{i+j+l}\),顺便我们的\(g_{i,j,l,3}\)里面算的时候默认所有颜色的物品等价,所以要乘上后面三个阶乘
//minamoto
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
int read(){
R int res,f=1;R char ch;
while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
return res*f;
}
const int N=505,P=998244353;
inline void upd(R int &x,R int y){(x+=y)>=P?x-=P:0;}
inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;}
inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;}
inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
int ksm(R int x,R int y){
R int res=1;
for(;y;y>>=1,x=mul(x,x))(y&1)?res=mul(res,x):0;
return res;
}
struct eg{int v,nx;}e[N<<1];int head[N],tot;
inline void Add(R int u,R int v){e[++tot]={v,head[u]},head[u]=tot;}
int fac[15],bin[15],sz[N],f[N][21][21][3],g[15][15][15][5],sum[3][15][15];
int n,k;
void dfs(int u,int fa){
f[u][0][0][0]=1,sz[u]=1;
go(u)if(v!=fa){
dfs(v,u);
fd(a,min(sz[u],k),0)fd(c,min(sz[u],k),0)if(f[u][a][c][0]||f[u][a][c][1]||f[u][a][c][2]){
R int x=f[u][a][c][0],y=f[u][a][c][1],z=f[u][a][c][2];
f[u][a][c][0]=f[u][a][c][1]=f[u][a][c][2]=0;
fd(b,min(sz[v],k-a),0)fd(d,min(sz[v],k-a),0)if(f[v][b][d][0]||f[v][b][d][1]||f[v][b][d][2]){
upd(f[u][a+b][c+d][0],mul(x,add(f[v][b][d][0],f[v][b][d][2]))),
upd(f[u][a+b+2][c+d][1],mul(x,f[v][b][d][0])),
upd(f[u][a+b+1][c+d][1],mul(x,f[v][b][d][1])),
upd(f[u][a+b][c+d][1],mul(y,add(f[v][b][d][0],f[v][b][d][2]))),
upd(f[u][a+b][c+d+1][2],mul(y,f[v][b][d][1])),
upd(f[u][a+b+1][c+d+1][2],mul(y,f[v][b][d][0])),
upd(f[u][a+b][c+d][2],mul(z,add(f[v][b][d][0],f[v][b][d][2])));
}
}
sz[u]+=sz[v];
}
fd(a,min(sz[u],k),0)fd(c,min(sz[u],k),0)upd(f[u][a][c+1][2],f[u][a][c][1]);
}
int main(){
// freopen("testdata.in","r",stdin);
n=read(),k=read(),fac[0]=bin[0]=1;
fp(i,1,k)fac[i]=mul(fac[i-1],i),bin[i]=add(bin[i-1],bin[i-1]);
fp(T,0,2){
tot=0,memset(head,0,(n+1)<<2);
for(R int i=1,u,v;i<n;++i)u=read(),v=read(),Add(u,v),Add(v,u);
memset(f,0,sizeof(f));
dfs(1,0);
fd(i,k,0)fd(j,k,0)sum[T][i][j]=add(f[1][i][j][0],f[1][i][j][2]);
}
g[1][0][0][0]=g[1][0][0][3]=1,
g[0][1][0][1]=g[0][1][0][3]=1,
g[0][0][1][2]=g[0][0][1][3]=1;
fp(i,0,k)fp(j,0,k-i)fp(l,0,k-i-j)if(i+j+l>1){
fp(op,0,2){
R int res=0;
if(op==0&&i)upd(res,dec(g[i-1][j][l][3],g[i-1][j][l][0]));
if(op==1&&j)upd(res,dec(g[i][j-1][l][3],g[i][j-1][l][1]));
if(op==2&&l)upd(res,dec(g[i][j][l-1][3],g[i][j][l-1][2]));
g[i][j][l][op]=res,upd(g[i][j][l][3],res);
}
}
int res=0;
fp(x,0,k)fp(y,0,k-x){
R int z=k-x-y;
fp(i,0,x)if(sum[0][x][i])
fp(j,0,y)if(sum[1][y][j])
fp(l,0,z)if(sum[2][z][l])
upd(res,1ll*sum[0][x][i]*sum[1][y][j]%P*sum[2][z][l]%P*g[i][j][l][3]%P*bin[i+j+l]%P*fac[i]%P*fac[j]%P*fac[l]%P);
}
printf("%d\n",res);
return 0;
}
\(F\) 最后之作
我感觉我已经是个废人了……
注意\(g_{i,j}\)表示的是\(s_1[i,j],s_2[i,j],...,s_n[i,j]\)这\(n\)个串中本质不同的串的个数,也就是说这个值值域为\([1,n]\)
我们设\(f_i\)表示前\(i\)个串划分的最大答案,那么转移显然
\]
把柿子化一下
\]
也就是说,如果不考虑\(p\times g_{j,i}\)这一项的话,这就是一个以\(i\)为自变量的一次函数。若干个一次函数取最大值,直接上李超线段树就行了
然而\(p\times g_{j,i}\)却使这个转移变得非常辣手……因为这个时候它就是一个一次函数加一个分段函数的和了
等会儿?分段函数?仔细看看啊……\(g_{i,j}\)的取值最多只有\(n\)个,而且对于一个固定的\(i\),\(g_{i,j}\)显然是递减的,所以\(g_{i,j}\)可以看做一个分段函数
那么我们对于每一段分别考虑就可以了呀。我们开\(n\)棵线段树,第\(k\)棵线段树上记录所有满足\(g_{j,i}=k\)的线段
对于每一个\(i\),预处理出所有\(g_{j,i}\)变化的位置(怎么处理待会儿会说),记\(pos_{i,j}\)表示\(g_{pos_j,i}=j\)且\(g_{pos_j+1,i}<j\)(也就是说\(g\)的值的\(j\)和\(j-1\)之间的分界点),那么我们把所有\(pos_{i-1,j}\)到\(pos_{i,j}\)之间的线段全都加入第\(j\)棵线段树就行了
等会儿?你这样会不会有点问题?比方说我现在满足\(g_{j,i}=k\),然后你把第\(j\)条线段加入了第\(k\)棵线段树,那么当你做到\(i+1\)的时候,\(g_{j,i+1}\)有可能会大于\(k\)啊?(比方说\(g_{j,i+1}=k+1\)),你线段树上存的线段就错掉了啊?
这种情况是不需要担心的,首先按照我们上面的处理过程,第\(k+1\)棵线段树也就加入这条线段。那么这两棵线段树里算出来的值是一样的,只有\(p\times g_{j,i}\)不一样。因为\(p\geq 0\),所以有\(p\times k+1\)就绝对不会用\(p\times k\)来更新答案
这样的话,每条线段会被加入\(n\)次,总共有\(len\)条线段,总复杂度就是\(O(nlen\log len)\)
然后就是这个预处理分界点的问题,如果用后缀数组,直接求出\(height\)之后瞎搞就可以了。如果用\(SAM\)的话,我们对所有串建一个广义\(SAM\),然后把\(parent\)树树剖了,把所有\(n\)个串中前缀\(s[1,i]\)对应的节点按\(dfs\)序排个序,那么相邻节点的\(LCA\)的深度就代表这两个串第一次相等的长度,那么在这之前它们就都是不等的了(不明白的话可以看看代码,画个图理解)
然后……很多细节还是看代码好了……
//minamoto
#include<bits/stdc++.h>
#define R register
#define ll long long
#define inf 1e18
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
int read(){
R int res,f=1;R char ch;
while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
return res*f;
}
int read(char *s){
R int len=0;R char ch;while(((ch=getc())>'z'||ch<'a'));
for(s[++len]=ch;(ch=getc())>='a'&&ch<='z';s[++len]=ch);
return s[len+1]='\0',len;
}
const int N=2e6+5;
int ch[N][26],l[N],fa[N],pos[N],cnt=1,las;
void ins(int c){
int p=las,np=las=++cnt;l[np]=l[p]+1;
for(;p&&!ch[p][c];p=fa[p])ch[p][c]=np;
if(!p)fa[np]=1;
else{
int q=ch[p][c];
if(l[q]==l[p]+1)fa[np]=q;
else{
int nq=++cnt;l[nq]=l[p]+1;
memcpy(ch[nq],ch[q],104);
fa[nq]=fa[q],fa[q]=fa[np]=nq;
for(;ch[p][c]==q;p=fa[p])ch[p][c]=nq;
}
}
}
struct eg{int v,nx;}e[N<<1];int head[N],tot;
inline void add(R int u,R int v){e[++tot]={v,head[u]},head[u]=tot;}
int dep[N],sz[N],son[N],top[N],dfn[N],tim;
inline bool cmp(const int &x,const int &y){return dfn[x]<dfn[y];}
void dfs1(int u){
sz[u]=1,dep[u]=dep[fa[u]]+1;
go(u){
dfs1(v),sz[u]+=sz[v];
sz[v]>sz[son[u]]?son[u]=v:0;
}
}
void dfs2(int u,int t){
top[u]=t,dfn[u]=++tim;
if(!son[u])return;
dfs2(son[u],t);
go(u)if(!top[v])dfs2(v,v);
}
int LCA(R int u,R int v){
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]])swap(u,v);
u=fa[top[u]];
}return dep[u]<dep[v]?u:v;
}
struct node{
node *lc,*rc;ll b,k,lv,rv;
inline void ins(R ll bb,R ll kk,R int l,R int r){b=bb,k=kk,lv=k*l+b,rv=k*r+b;}
inline ll calc(R int x){return k*x+b;}
}pool[N<<2],*rt[N];int num;
inline node *newnode(){return &pool[num++];}
ll b,k,bb,kk,res;int x;
void build(node* &p,int l,int r){
p=newnode(),p->k=0,p->b=p->lv=p->rv=inf;
if(l==r)return;
int mid=(l+r)>>1;
build(p->lc,l,mid),build(p->rc,mid+1,r);
}
void query(node *p,int l,int r){
cmin(res,p->calc(x));if(l==r)return;
int mid=(l+r)>>1;
x<=mid?query(p->lc,l,mid):query(p->rc,mid+1,r);
}
void update(node *p,int l,int r){
ll lv=k*l+b,rv=k*r+b;
if(lv>=p->lv&&rv>=p->rv)return;
if(lv<p->lv&&rv<p->rv)return p->ins(b,k,l,r),void();
int mid=(l+r)>>1;double x=(b-p->b)/(p->k-k);
if(lv<=p->lv){
if(x<=mid)update(p->lc,l,mid);
else bb=p->b,kk=p->k,p->ins(b,k,l,r),b=bb,k=kk,update(p->rc,mid+1,r);
}else{
if(x<=mid)bb=p->b,kk=p->k,p->ins(b,k,l,r),b=bb,k=kk,update(p->lc,l,mid);
else update(p->rc,mid+1,r);
}
}
int A[N],B[N],f[2][N];char s[N];
int n,len,p,t;ll dp[N];
int main(){
// freopen("testdata.in","r",stdin);
n=read(),len=read(),p=read();
fp(i,1,len)A[i]=read();fp(i,1,len)B[i]=read();
fp(i,1,n){
read(s);las=1;
fp(j,1,len)ins(s[j]-'a'),pos[++t]=las;
}
fp(i,2,cnt)add(fa[i],i);
dfs1(1),dfs2(1,1);
fp(i,1,n)build(rt[i],1,len);
for(R int i=1,t=0;i<=len;++i,t^=1){
fp(j,1,n)f[t][j]=pos[(j-1)*len+i];
sort(f[t]+1,f[t]+1+n,cmp);
fd(j,n,2)f[t][j]=l[LCA(f[t][j],f[t][j-1])];
f[t][1]=i;sort(f[t]+2,f[t]+1+n);
fp(j,2,n)f[t][j]=i-f[t][j];
fp(j,1,n)fp(l,f[t^1][j],f[t][j]-1){
b=B[l+1]-dp[l]-1ll*l*A[l+1],
k=A[l+1];
update(rt[j],1,len);
}
dp[i]=-inf,x=i;
fp(j,1,n)if(f[j]!=f[j+1])res=inf,query(rt[j],1,len),cmax(dp[i],1ll*p*j-res);
}
printf("%lld\n",dp[len]);
return 0;
}
Wannafly挑战赛29题解的更多相关文章
- Wannafly挑战赛26题解
为啥混进了几道不是魔禁的题--出题人太不敬业了-- 传送门 \(A\) 御坂网络 为啥没有番外个体和整体意志呢 暴力模拟就好了,这个要是都打错我干脆滚回去学文化课算了 //minamoto #incl ...
- Wannafly挑战赛21A
题目链接 Wannafly挑战赛21A 题解 代码 #include <cstdio> #include <cmath> #define MAX 1000005 #define ...
- Wannafly 挑战赛 19 参考题解
这一次的 Wannafly 挑战赛题目是我出的,除了第一题,剩余的题目好像对大部分算法竞赛者来说好像都不是特别友好,但是个人感觉题目质量还是过得去的,下面是题目链接以及题解. [题目链接] Wanna ...
- Wannafly挑战赛13 zzf的好矩阵 题解 答案解释
Wannafly挑战赛13 zzf的好矩阵 题解 文章目录 Wannafly挑战赛13 zzf的好矩阵 题解 分析 结论1 结论2 结论3 C数组对应带子说明 空白长度论述 后续黑色长度论述 能&qu ...
- Wannafly挑战赛27
Wannafly挑战赛27 我打的第一场$Wannafly$是第25场,$T2$竟然出了一个几何题?而且还把我好不容易升上绿的$Rating$又降回了蓝名...之后再不敢打$Wannafly$了. 由 ...
- 【Wannafly挑战赛4】F 线路规划 倍增+Kruskal+归并
[Wannafly挑战赛4]F 线路规划 题目描述 Q国的监察院是一个神秘的组织.这个组织掌握了整个帝国的地下力量,监察着Q国的每一个人.监察院一共有N个成员,每一个成员都有且仅有1个直接上司,而他只 ...
- Wannafly挑战赛18 E 极差(线段树、单调栈)
Wannafly挑战赛18 E 极差 题意 给出三个长度为n的正整数序列,一个区间[L,R]的价值定义为:三个序列中,这个区间的极差(最大值与最小值之差)的乘积. 求所有区间的价值之和.答案对\(2^ ...
- Wannafly挑战赛26-F. msc的棋盘(模型转化+dp)及一类特殊的网络流问题
题目链接 https://www.nowcoder.com/acm/contest/212/F 题解 我们先考虑如果已知了数组 \(\{a_i\}\) 和 \(\{b_i\}\),如何判断其是否合法. ...
- Wannafly挑战赛21:C - 大水题
链接:Wannafly挑战赛21:C - 大水题 题意: 现在给你N个正整数ai,每个数给出一“好数程度” gi(数值相同但位置不同的数之间可能有不同的好数程度).对于在 i 位置的数,如果有一在j位 ...
随机推荐
- 模m的剩余类里的一切数与m的最大公约数相等
[模m的剩余类里的一切数与m的最大公约数相等] 设剩余类里的任意两元素,a.b.则: a=mq1+r1, b= mq2+r1. 根据上式可得,(a,m)=(m,r1), (b,m)=(m,r2).可推 ...
- 禁止ImageCapture自动启动
[禁止ImageCapture自动启动] 打开ImageCapture,点开左下角菜单,把Connecting this iPhone opens:的内容改为以下选项即可.
- ios的@property属性和@synthesize属性(转)
当你定义了一系列的变量时,需要写很多的getter和setter方法,而且它们的形式都是差不多的,,所以Xcode提供了@property 和@synthesize属性,@property用在 .h ...
- canvas动画--demo
canvas动画:bubble
- 读写大“二进制”文件,不必申请很大内存(fopen,fread,fwrite,fclose)
<?php /** * 读写大二进制文件,不必申请很大内存 * 只有读取到内容才创建文件 * 保证目录可写 * * @param string $srcPath 源文件路径 * @param s ...
- h5 时间控件问题,怎么设置type =datetime-local 的值
在js中设置自定义时间到date控件的方法: 1.在html5中定义时间控件 <input type="date" id="datePicker" val ...
- NormalMap原理详细解析
NormalMap的实现标志着对渲染流水线的各个环节以及矩阵变化有了正确和深入的认识.这里记录一下学习过程,以及关于NormalMap的诸多细节. 刚开始想要实现NormalMap程序的时候,查阅的是 ...
- YUI前端优化之Server篇
二.网站Server 篇:使用内容分发网络为文件头指定Expires或Cache-ControlGzip压缩文件内容配置ETag尽早刷新输出缓冲使用GET来完成AJAX请求 11.使用内容分发网络 用 ...
- CentOS7.2部署KVM虚拟机
转自:http://www.linuxidc.com/Linux/2017-01/140007.htm 学习了关于PostGis.OSM数据以及Mapnik相关内容,接下来将利用假期重点学习Postg ...
- 2016年,你读过的最好的IT技术书有哪几本?
def 程序员 原文 https://www.zhihu.com/question/54350343 陈硕 等 54 人赞同了该回答 1 知乎 陈硕大牛的 服务器多线程编程muduo 输的好不好可能更 ...