计蒜客 2016计蒜之道比赛 初赛第四场 记录
T1 淘宝流量分配
在每年的淘宝“双十一”时,访问量都会暴涨,服务器的请求会被流量分配程序按照一定策略,分发给不同的进程去处理。有一类请求,有两个进程可以接受分发的请求,其中一个进程所在服务器的配置、网络传输性能等都要优于另一个进程。流量分发程序可以知道队列中每个任务的预计处理时间,每次都会尽可能将队列中预计处理时间更多的任务分配给性能更优的进程。
假设队列当前一共有 nn 个任务待分配,第 ii 个任务的预计处理时间为 a_i(1 \leq i \leq n)ai(1≤i≤n)。由于服务存在冷启动问题,越靠后的进程,预计处理时间越短。而队列中的 最后一个任务 因为比较特殊,预计处理时间和之前的任务 无关。即前 n-1n−1 个任务的预计处理时间满足 a_1 > a_2 > ... > a_{n-1}a1>a2>...>an−1。现在要从中选出一个任务列表,不能有任何两个任务在原队列中相邻。计算选出的任务队列预计处理时间之和的最大值。
输入格式
输入第一行有一个整数 n(1 \leq n \leq 10^{4})n(1≤n≤104),表示任务总数。
输入第二行有 nn 个整数 a_i(0 \leq a_i \leq 10^{4})ai(0≤ai≤104),表示每个任务的预计处理时间。
输出格式
输出一个整数,表示选出的任务的预计处理时间之和的最大值。
样例输入1
5 4 3 2 1 5
样例输出1
11
样例输入2
4 5 3 1 9
样例输出2
14
提示信息
对于第一组样例,选出的总预计处理时间最大为 4+2+5=114+2+5=11。
对于第二组样例,选出的总预计处理时间最大为 5+9=145+9=14。
简单的动态规划问题
1 /*by SilverN*/ 2 #include<iostream> 3 #include<cstdio> 4 #include<cmath> 5 #include<cstring> 6 #include<algorithm> 7 using namespace std; 8 long long f[20000]; 9 int n; 10 int a; 11 int main(){ 12 scanf("%d",&n); 13 int i,j; 14 scanf("%d",&a); 15 f[0]=0; 16 f[1]=a; 17 if(n==1){ 18 printf("%d\n",a); 19 return 0; 20 } 21 for(i=2;i<=n;i++){ 22 scanf("%d",&a); 23 for(j=0;j<i-1;j++){ 24 f[i]=max(f[i],f[j]); 25 } 26 f[i]+=a; 27 } 28 int mx=1; 29 for(i=2;i<=n;i++){ 30 if(f[i]>f[mx])mx=i; 31 } 32 cout<<f[mx]<<endl; 33 return 0; 34 }
T2-T4 遗失的支付宝密码
某用户忘记了支付宝的登录密码,他只记得自己的密码满足以下几个条件:
-
密码中最多有 mm 种不同的字符;
-
密码的最大长度为 nn,但不能为空;
-
密码的任意一个前缀都 不是 一个 squaresquare。squaresquare 的定义如下:
a. 字符串的长度 ll 是偶数;
b. 字符串的长度为 \frac{l}{2}2l 的前缀和长度为 \frac{l}{2}2l 的后缀相同。
比如,abab
是一个 squaresquare,而 abba
则不是。
请问符合条件的密码有多少个。因为数量很多,只需要输出总个数对 2^{32}232 取模的结果即可。
输入格式
输入第一行包含两个整数 n,m(n,m \geq 1)n,m(n,m≥1),nn 为密码的最大长度,密码中最多有 mm 种不同的字符。
对于简单版本:n \leq 5n≤5,m \leq 10m≤10;
对于中等版本:n \leq 40n≤40,m \leq 10^9m≤109;
对于困难版本:n \leq 100n≤100,m \leq 10^9m≤109。
输出格式
输出一个整数,表示满足条件的密码数量,结果对 2^{32}232 取模。
样例输入1
3 2
样例输出1
8
样例输入2
4 5
样例输出2
605
提示信息
对于第一组样例,假设字符集为 \{a,b\}{a,b}。长度为 33 以内且符合条件的密码有如下 88 个:
a
b
ab
ba
aba
abb
baa
bab
只做出了简单难度的。
1 #include<iostream> 2 #include<cstdio> 3 #include<cmath> 4 #include<cstring> 5 #include<algorithm> 6 using namespace std; 7 const int mxn=100; 8 long long sum=0; 9 long long f[mxn]; 10 int m,n; 11 int main(){ 12 scanf("%d%d",&n,&m); 13 int i,j; 14 f[0]=1; 15 for(i=1;i<=n;i++){ 16 if(i&1)f[i]=f[i-1]*m; 17 else f[i]=f[i-1]*m-f[i/2]; 18 sum+=f[i]; 19 } 20 cout<<sum<<endl; 21 return 0; 22 }
后面的我直接放官方题解上来
第四场
淘宝流量分配
枚举最后一个任务是否选择,之后根据最后一个任务是否选择来分别求解前面序列能选取的最大值即可。
遗失的支付宝密码
子问题 1
用 O(m^n)O(mn) 暴力枚举所有答案,然后删掉不合法的字符串,统计个数即可。
子问题 2
只要知道了每个位置的等价关系,我们事实上就确定了这个字符串。一种想法是直接枚举每个位置的相等关系,然后计数,但是这样复杂度是 O(Bell(n))O(Bell(n)) 的,显然会 TLE。另一个想法是容斥,注意到只有长度是偶数的前缀才会成为 square,大概有 n/2n/2 个。就可以 2^{n/2}2n/2 枚举哪些前缀一定是 squaresquare,做容斥。答案就是 m^n-mn−至少有 11 个前缀是 square ++ 至少有 22 个前缀是 square -− …。
题外话:其实答案最后答案的结构显然是 c_1 \cdot m^1 + c_2 \cdot m^2 + ... + c_n \cdot m^nc1⋅m1+c2⋅m2+...+cn⋅mn 这样的,系数 c_ici 只和nn 有关,于是可以本地打表出来。这样就可以很快解决这个 case。
子问题 3
考虑 dp(i,S)dp(i,S) 表示当前处理完前 ii 个偶数长度的前缀,并且第 ii 个一定是一个 square,ii 后面那些位置都不是 square,当前所有位置的等价关系是 SS 的方案数。
那么如果不考虑 ii 后面那些位置都不是 square,方案数是 m^{2i-cnt}(m^0+m^1+...+m^{n-2i})m2i−cnt(m0+m1+...+mn−2i),其中 cntcnt 是当前状态中联通分量的数目,考虑定义只需要减去 dp(j,S'),i<jdp(j,S′),i<j 即可。这样直接算 SS的状态应该会爆炸。注意到可以将 SS 做最小表示,通过简单的计算发现 n=100n=100 时最多只有 4330843308 种不同的状态,在这个时限下用 map 搞搞就好了。