SDOI2016 R1 day2 解题报告(bzoj4516,bzoj4517,bzoj4518)
2016-04-12 13:41
399 查看
感言什么的 之后补游记吧
只能说考场没AK,我是傻逼
100%:n<=100000,1<=字符集<=10^9
做过【bzoj3926】[Zjoi20150]诸神眷顾的幻想乡的,会发现这两个题神似,并且这个题还是诸神眷顾的幻想乡的弱化版。
然而数据范围中的字符集太大,貌似SAM不可取?
出题人faebdc给的做法是求反串的SA,然后在SA中的height数组中一个个删除后缀,删除时减去当前后缀的相邻后缀的lcp,再加上新的相邻一对的lcp,更新答案。
SAM的做法就很简单了…边数和点数竟然都是O(n)的,map能过!
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))。然后这题我开考半小时就水掉了…
下面是考场代码
100%:1<=m<=n<=3000,ai>0,∑ai<=30000
考场上我傻X,写的DP是三维的,40分。
化简一下目标函数。其实是要求每段的平方和最小。
f[i][j]为当前走到第i个,当前是第j段的最小平方和,很容易写出方程:
f[i][j]=min{f[k][j−1]+(s[i]−s[k])2}
这就60分。
发现可以斜率优化。固定j之后,发现满足这个关系:
(fq,j−1+S2q)−(fw,j−1+S2w)Sq−Sw<2Si
其中q优于w且q>w。
然后因为二维的,需要记录上一层的函数值,被这个坑的调了半天……
最后插一句,这个题骗分在bzoj能A掉…考场上也能90分……
只能说考场没AK,我是傻逼
生成魔咒
题意
给一个字符串,初始为空串,然后往字符串尾部依次添加字符,每添加一个字符询问当前串中本质不同的子串的个数。数据范围
60%:n<=1000100%: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<=100070%: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
* 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
[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<=100100%:1<=m<=n<=3000,ai>0,∑ai<=30000
考场上我傻X,写的DP是三维的,40分。
化简一下目标函数。其实是要求每段的平方和最小。
f[i][j]为当前走到第i个,当前是第j段的最小平方和,很容易写出方程:
f[i][j]=min{f[k][j−1]+(s[i]−s[k])2}
这就60分。
发现可以斜率优化。固定j之后,发现满足这个关系:
(fq,j−1+S2q)−(fw,j−1+S2w)Sq−Sw<2Si
其中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 * m - pf(s )); return 0; }
最后插一句,这个题骗分在bzoj能A掉…考场上也能90分……
相关文章推荐
- yum安装不能正常使用的情况
- 关于大型网站技术演进的思考(八)--存储的瓶颈终篇(8)
- 文件过滤驱动和设备驱动的区别
- 关于 UITapGestureRecognizer 一些注意的地方
- Elasticsearch 2.2.0 插件篇:插件清单
- Bootstrap CSS 表单
- 苹果针对 IAP 11.12的反馈
- 《黑客帝国》里的数字雨
- 模式识别(Pattern Recognition)学习笔记(三)——最小错误率贝叶斯决策
- hdoj 1220Cube
- 苹果所有常用证书,appID,Provisioning Profiles配置说明及制作图文教程(精)
- 关于大型网站技术演进的思考(六)--存储的瓶颈(6)
- 关于大型网站技术演进的思考(七)--存储的瓶颈(7)
- js菜鸟备忘
- MYCAT介绍
- 腾讯2016春季校园实习招聘技术岗初试(一面)问题汇总(CC++后台)
- iOS相机相册调用 — UIImagePickerController
- Majority Element
- (转)php5.3中的连接MSSQLSERVER(非PDO方式)
- 根据git的hash值创建分支