好久没写题解了,随便写一发SDOI的题解吧,你问我为什么身为JL的选手不写JLOI,因为我太弱了不会啊QWQ
T1 生成魔咒:http://www.lydsy.com/JudgeOnline/problem.php?id=4516
题解
后缀自动机模板题,在加入一个值时直接得出已经加入自动机的字符的子串个数,后缀自动机用map来维护
#include <bits/stdc++.h>
#define N 200010
using namespace std;
typedef long long ll;
int n;
struct sam
{
int cnt,last,p,q,np,nq;
ll ans;
int len[N],fa[N];
ll size[N];
map<int,int>son[N];
map<int,int>::iterator it;
sam(){last=cnt=1,size[1]=1;}
void Insert(int x)
{
p=last,last=np=++cnt,len[np]=len[p]+1;
while(p&&!son[p][x])son[p][x]=np,size[np]+=size[p],p=fa[p];
ans+=size[np];
if(!p)fa[np]=1;
else{
q=son[p][x];
if(len[q]==len[p]+1)fa[np]=q;
else{
nq=++cnt;
len[nq]=len[p]+1;
fa[nq]=fa[q];
for(it=son[q].begin();it!=son[q].end();it++)
son[nq][it->first]=it->second;
fa[q]=fa[np]=nq;
while(son[p][x]==q)son[p][x]=nq,size[q]-=size[p],size[nq]+=size[p],p=fa[p];
}
}
}
void build()
{
for(int x,i=1;i<=n;i++)
{
scanf("%d",&x);
Insert(x);
printf("%lld\n",ans);
}
}
}sam;
int main()
{
// freopen("tt.in","r",stdin);
scanf("%d",&n);
sam.build();
return 0;
}
T2 排列计数:http://www.lydsy.com/JudgeOnline/problem.php?id=4517
题解
简单排列组合+dp xjk教我了一个据说他初中学的公式(orz)f[i]表示有i个数且a[j]!=j(0< j=i),f[i]=(f[i-1]+f[i-2])*(i-1)
预处理出这个式子之后,对于一组询问(n,m)即为
Cmn∗f[n−m]
用卢卡斯定理随便求一下组合数,即可求得答案
#include <bits/stdc++.h>
#define LEN 1<<16
char getc()
{
static char *S,*T,buf[LEN];
if(S==T)
{
T=(S=buf)+fread(buf,1,LEN,stdin);
if(S==T)return EOF;
}
return *S++;
}
int read()
{
static char ch;
static int D,F;
F=1;
while(!isdigit(ch=getc())&&ch!='-');
if(ch=='-')F=-1,ch=getc();
for(D=ch-'0';isdigit(ch=getc());)
D=D*10+ch-'0';
return D*F;
}
#define N 1000000
#define mod 1000000007
using namespace std;
typedef long long ll;
int n,m;
int f[N+10];
int a[N+10];
void init()
{
f[0]=1,f[2]=1,f[3]=2;
for(int i=4;i<=N;i++)
f[i]=(ll)(f[i-1]+f[i-2])*(i-1)%mod;
a[0]=a[1]=1;
for(int i=2;i<=N;i++)
a[i]=(ll)a[i-1]*i%mod;
}
int qpow(int x,int y)
{
int ans=1;
while(y)
{
if(y&1)ans=(ll)ans*x%mod;
x=(ll)x*x%mod;
y>>=1;
}
return ans;
}
int lucas(int x,int y)
{
if(y==0)return 1;
return (ll)a[x]*qpow(a[y],mod-2)%mod*qpow(a[x-y],mod-2)%mod*lucas(x/mod,y/mod)%mod;
}
int main()
{
// freopen("tt.in","r",stdin);
init();
int TOT=read();
while(TOT--)
{
n=read(),m=read();
printf("%d\n",(ll)lucas(n,m)*f[n-m]%mod);
}
return 0;
}
T3 征途:http://www.lydsy.com/JudgeOnline/problem.php?id=4518
题意是将n个数分成m组,求出m组数的方差
设第i组数的和为
yi
∑mi=1yi=∑ni=1xi
M=∑mi=1yim=∑ni=1xim
ans=∑mi=1(yi−M)2m=m∗M2−2∗M∗∑mi=1yi+∑mi=1y2im=m∗M2−2∗M∗∑ni=1xi+∑mi=1y2im
这道题就变为求
∑mi=1y2i
的最小值,其它元素可以直接处理出来
这里用一个简单的dp就能求出来:f[i][j]表示第i天在第j个休息站休息,sum[i]表示从1到第i个休息站的路程
f[i][j]=min(f[i-1][k]f[i-1][k]+(sum[j]-sum[k])*(sum[j]-sum[k]))(0< k<=j)
但这是
O(n3)
的复杂度,还需要用斜率优化将复杂度降到
O(n2)
#include <bits/stdc++.h>
#define N 3010
using namespace std;
typedef long long ll;
int n,m;
int a[N],sum[N];
ll f[N][N];
int que[N];
double calc(int s,int x,int y)
{
return (f[s][y]+sum[y]*sum[y]-f[s][x]-sum[x]*sum[x])/(2*(sum[y]-sum[x]));
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
scanf("%d",&a[i]),sum[i]=sum[i-1]+a[i];
ll ans=sum[n]*sum[n]-2*sum[n]*sum[n];
memset(f,-1,sizeof(f));
f[0][0]=0;
for(int i=1;i<=m;i++)
{
int head=1,tail=0;
que[++tail]=i-1;
for(int j=i;j<=n;j++)
{
while(head<tail&&calc(i-1,que[head],que[head+1])<sum[j])
head++;
int x=que[head];
f[i][j]=f[i-1][x]+sum[j]*sum[j]+sum[x]*sum[x]-2*sum[j]*sum[x];
if(f[i-1][j]==-1)continue;
while(head<tail&&calc(i-1,que[tail],j)<calc(i-1,que[tail-1],que[tail]))
tail--;
que[++tail]=j;
}
}
cout<<ans+f[m][n]*m<<endl;
return 0;
}