河南省队胡扯第一场解题报告

     不提前完成任务的报应就是这样……昨天晚上熬夜赶题面捏数据,结果早上起来发现各种漏洞,最后两题还没时间捏数据了,算是彻底失败吧= =|||

     先把题解写出来,下午再慢慢捏数据好了。

 A

     第一题大概意思是维护一个集合,要求实现两种操作:添加一个元素 和 删去其中较大的一半(注意是下取整)。最后还要求能够线性地输出(虽然排序后再输出没有被卡掉……)。

     算法一是直接用一棵平衡树维护,因为每个元素只可能被删除一次,所以直接暴力删除就可以。

     然而平衡树常数比较大(虽然我已经没有时间去造针对平衡树的数据了……),可以考虑用一个最大堆实现,删除时直接依次弹出就可以了。

     我为正解准备的数据范围似乎也不怎么合理,只有10^7……其实这个算法是在《算法导论》摊还分析一节中的习题,我们只需要维护一个无序数列,实现一个$O(|S|)$的删除操作,而每次插入的时候直接将元素附加在数组末尾,就可以在关于操作数呈线性的时间内完成所有操作了。这个同样可以通过“每个元素只会被删除一次”来证明——我们可以将每次删除操作需要的时间附加在删除的那些元素对应的插入操作上,由于每次删除的元素个数也是$O(|S|)$,很明显所有删除操作附加在插入操作上的时间是常数级别的。

     对于无序数列的删除操作可以在《算法导论》上的“顺序统计”一节找到。具体地,我们可以通过类似快速排序的随机算法在线性的期望时间内求出需要删除的最小的元素,将比这个元素小的元素全部移动到数组的前半部分就可以了。

 

 1 #include 
 2 #include 
 3 
 4 const int maxn = 10000003;
 5 typedef long long LL;
 6 
 7 int A[maxn], *it = A, N;
 8 bool exist[maxn];
 9 
10 int main(){
11 #ifdef DEBUG
12     freopen("test.txt""r", stdin);
13 #endif
14     int *i, M, *t, mid, a, x;
15     unsigned rnd, C;
16     scanf("%d%d%u%d%u", &N, &M, &rnd, &a, &C);
17     x = a;
18     while(M--){
19         rnd += (rnd << 2) + 1;
20         if(rnd & C)*(it++) = x, x = (LL)x * a % N;
21         else{
22             t = A + (it - A + 1) / 2;
23             std::nth_element(A, t, it);mid = *t;
24             for(i = A;i < t;++i)if(*i >= mid)std::swap(*i, *(--it));
25             it = t;
26         }
27     }
28     printf("%d\n"int(it - A));
29     for(i = A;i < it;++i)exist[*i] = true;
30     for(int j = 1;j <= N;++j)if(exist[j])printf("%d ", j);
31     puts("");
32     return 0;
33 }
View Code

B

     这题其实是我把一个idea强行套在了一个经典算法上……所以数据范围出得有点牵强,非常抱歉……

     第一层数据范围是强行留给只会倍增LCA的同学的(比如一两个个月前的我)。

     第二层数据,由于询问次数比较大(不过我猜我还是卡不掉倍增……),可以先预处理出dfs序列,构造出ST表来实现$O(1)$查询。

     第三层数据……嗯……说出来你们不要打我啊!= = 我们来看数据中的N,都符合$3 * 2^i$的形式,所以此中必有蹊跷!观察那个“强制在线”的递推式,u_i = X^{\frac{N}{3} * Ans_{i-1} + k} * i \mod Nv_i = Y^{\frac{N}{3} * Ans_{i-1} + k} * i \mod N,可以发现$\frac{N}{3}$恰好是$\phi(N)$的表达式。那么递推式就变成了这样……

$u_i = X^{\phi(N) * Ans_{i-1}} * X^k * i \mod N$,或$u_i = X^k * i \mod N$。那么我们就可以用Tarjan LCA离线做了……虽然数据范围很怪异,但是……领会精神,领会精神……

C

     我思考了好久的idea竟被出成了这个鬼样子……= =还是数据范围的问题,看来不先写std真是不能乱定数据范围啊= =

     算法一:$$F_i = 1 (i = 0 \mod M \lor i < M);$$

                $$F_i = F_{i-1} + F_{i-M}(otherwise)$$

直接递推,不说了;

     算法二:算法一 + 高精度.(其实我实在不想加上这一层数据……)

     算法三:不难发现,从M开始,递推式的选择是以M为周期的。尝试把从0开始的数列中的元素每M个一行铺成矩阵,那么递推式就可以写成这样:

$$F_{i,j} = 1 (i = 0 \lor j = 0)$$

$$F_{i,j} = F_{i-1,j} + F_{i,j-1}$$

     发现递推式很熟悉?没错这个数列其实就是组合数……具体来说是$F_{i,j} = \tbinom{i}{i+j} = \frac{(i+j)!}{i! j!}$ 用这里介绍的阶乘质因数分解 求组合数就可以了。


 

posted @ 2015-05-25 12:15 Asm.Definer 阅读(...) 评论(...) 编辑 收藏