感言什么的 之后补游记吧
只能说考场没AK,我是傻逼
给一个字符串,初始为空串,然后往字符串尾部依次添加字符,每添加一个字符询问当前串中本质不同的子串的个数。
60%:n<=1000
100%:n<=100000,1<=字符集<=10^9
做过【bzoj3926】[Zjoi20150]诸神眷顾的幻想乡的,会发现这两个题神似,并且这个题还是诸神眷顾的幻想乡的弱化版。
然而数据范围中的字符集太大,貌似SAM不可取?
出题人faebdc给的做法是求反串的SA,然后在SA中的height数组中一个个删除后缀,删除时减去当前后缀的相邻后缀的lcp,再加上新的相邻一对的lcp,更新答案。
SAM的做法就很简单了…边数和点数竟然都是O(n)的,map能过!
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<map>
using namespace std;
typedef long long LL;
const int SZ = 1000010;
struct node{
map<int,node*> ch;
node *par;
int val;
}T[SZ], *last, *root;
int Tcnt = 0;
node* newnode(int x)
{
node *k = T + (Tcnt ++);
k -> val = x;
k -> par = NULL;
k -> ch.clear();
return k;
}
LL ans = 0;
LL get_ans(node *p)
{
return p -> val - p -> par -> val;
}
void insert(int x)
{
node *p = last,*np = newnode(p -> val + 1);
while(p && !p -> ch[x])
p -> ch[x] = np,p = p -> par;
if(!p)
np -> par = root,ans += get_ans(np);
else
{
node *q = p -> ch[x];
if(q -> val == p -> val + 1)
np -> par = q,ans += get_ans(np);
else
{
node *nq = newnode(p -> val + 1);
nq -> ch = q -> ch;
nq -> par = q -> par; ans += get_ans(nq);
np -> par = nq; ans += get_ans(np);
ans -= get_ans(q); q -> par = nq; ans += get_ans(q);
while(p && p -> ch[x] == q)
p -> ch[x] = nq,p = p -> par;
}
}
last = np;
}
void init()
{
root = newnode(0);
last = root;
}
int main()
{
init();
int n;
scanf("%d",&n);
for(int i = 1;i <= n;i ++)
{
int x;
scanf("%d",&x);
insert(x);
printf("%lld\n",ans);
}
return 0;
}
/* 7 1 2 3 3 3 1 2 */
求n的排列中,有多少个排列满足恰好有m个 a[i]=i 。多组数据。
5
1 0
1 1
5 2
100 50
10000 5000
0
1
20
578028887
60695423
60%:T<=1000,n,m<=1000
70%:T<=500000,n,m<=1000
100%:T<=500000,n,m<=1000000
全排列二十分。状压DP30分(然而这三十分好像都是打表)。出题人给的容斥原理是70分算法,就是求 ∑Cin(−1)i∗(n−i)! 。然而预处理一下就A掉了。
不过我脸好知道错排…答案是 Cmn∗f(n−m) ,其中f是错排公式 f(n)=(n−1)∗(f(n−1)+f(n−2)) 。然后这题我开考半小时就水掉了…
下面是考场代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
const int SZ = 1000010;
const int mod = 1000000007;
int f[SZ],fac[SZ],c[1010][1010];
void exgcd(LL a,LL b,LL &x,LL &y)
{
if(b == 0)
{
x = 1; y = 0; return ;
}
exgcd(b,a % b,x,y);
LL t = x; x = y; y = t - a / b * y;
}
LL ni(LL a)
{
LL x,y;
exgcd(a,mod,x,y);
return (x % mod + mod) % mod;
}
LL C(int n,int m)
{
return (LL)fac[n] * ni((LL)fac[n - m] * fac[m] % mod) % mod;
}
void scan(int &n)
{
n = 0;
char a = getchar();
bool flag = 0;
while(a < '0' || a > '9') { if(a == '-') flag = 1; a = getchar(); }
while(a >= '0' && a <= '9') { n = n * 10 + a - '0'; a = getchar(); }
if(flag) n = -n;
}
int main()
{
freopen("permutation.in","r",stdin);
freopen("permutation.out","w",stdout);
f[0] = 1; f[1] = 0;
for(int i = 2;i <= 1000000;i ++)
f[i] = (LL)(i - 1) * ((LL)f[i - 1] + f[i - 2]) % mod;
fac[0] = fac[1] = 1;
for(int i = 2;i <= 1000000;i ++)
fac[i] = (LL)i * fac[i - 1] % mod;
c[0][0] = 1;
for(int i = 1;i <= 1000;i ++)
{
c[i][0] = 1;
for(int j = 1;j <= i;j ++)
c[i][j] = (LL)(c[i - 1][j - 1] + c[i - 1][j]) % mod;
}
int T;
scan(T);
while(T --)
{
int n,m;
scan(n); scan(m);
if(n < m) puts("0");
else
{
if(n <= 1000 && m <= 1000)
printf("%I64d\n",(LL)c[n][m] * f[n - m] % mod);
else
printf("%I64d\n",(LL)C(n,m) * f[n - m] % mod);
}
}
fclose(stdin); fclose(stdout);
return 0;
}
/* 5 1 0 1 1 5 2 100 50 10000 5000 */
把n个数的数列分成m段,每段元素必须连续。每一段的权值是这一段中所有数之和,求最小化方差v。输出 v∗m2 ,这个数必定为整数。
60%:1<=m<=n<=100
100%:1<=m<=n<=3000, ai>0,∑ai<=30000
考场上我傻X,写的DP是三维的,40分。
化简一下目标函数。其实是要求每段的平方和最小。
f[i][j]为当前走到第i个,当前是第j段的最小平方和,很容易写出方程:
这就60分。
发现可以斜率优化。固定j之后,发现满足这个关系:
其中q优于w且 q>w 。
然后因为二维的,需要记录上一层的函数值,被这个坑的调了半天……
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<map>
using namespace std;
typedef long long LL;
const int SZ = 3010;
const LL INF = 10000000000000010ll;
LL f[SZ],a[SZ],s[SZ];
LL pf(LL x)
{
return x * x;
}
struct haha{
LL s,x;
}q[SZ];
int t = 0,w = 0;
double xl(haha q,haha w)
{
return (q.s - w.s * 1.0) / (q.x - w.x * 1.0);
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;i ++)
{
scanf("%lld",&a[i]);
s[i] = s[i - 1] + a[i];
}
for(int i = 1;i <= n;i ++)
f[i] = pf(s[i]);
for(int j = 2;j <= m;j ++)
{
t = 1; w = 0;
for(int i = 1;i <= n;i ++)
{
haha np = (haha){f[i] + pf(s[i]),s[i]};
while(t < w && xl(np,q[w]) < xl(q[w],q[w - 1])) w --;
q[++ w] = np;
while(t < w && xl(q[t + 1],q[t]) < 2 * s[i]) t ++;
f[i]=s[i]*s[i]+q[t].s-2*q[t].x*s[i];
}
}
printf("%lld",f[n] * m - pf(s[n]));
return 0;
}
最后插一句,这个题骗分在bzoj能A掉…考场上也能90分……