• 10月清北学堂培训 Day 5


    今天是廖俊豪老师的讲授~

    T1

    第一次想出正解

    30 pts:

    k <= 10,枚举如何把数放到矩阵中,O ( k ! );

    100 pts:

    对于矩阵的每一列,我们二分最小差异值,然后贪心去判断是否可行;

    贪心策略:从前往后找,如果有从某个数开始往后连续的 m 个数,这 m 个数的最大值 - 最小值 < k,那么就把这 m 个数放到同一行,最后判断是否能够凑出 n 行;

    std 标程:

    #include<bits/stdc++.h>
    using namespace std;
    
    const int maxn = 1e5 + 7;
    int n, m, k, v[maxn];
    
    int judge(int d)
    {
        int tmp = 0;
        for (int i=1; i+m-1<=k; ++i)
        {
            if (v[i+m-1] - v[i] <=d)
                ++tmp, i += m - 1;
        }
        if (tmp >= n) return 1;
        return 0;
    }
    
    int main()
    {
        freopen("a.in", "r", stdin);
        freopen("a.out", "w", stdout);
        scanf("%d%d%d", &k, &n, &m);
        for (int i=1; i<=k; ++i)
            scanf("%d", &v[i]);
        sort(v + 1, v + k + 1);
        int left = 0, right = 1e9;
        while (left < right)
        {
            int mid = (left + right) / 2;
            if (judge(mid))
                right = mid;
            else
                left = mid + 1;
        }
        printf("%d
    ", left);
        return 0;
    }

    我的拙码:

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    int read()
    {
        char ch=getchar();
        int a=0,x=1;
        while(ch<'0'||ch>'9')
        {
            if(ch=='-') x=-x;
            ch=getchar();
        }
        while(ch>='0'&&ch<='9')
        {
            a=(a<<1)+(a<<3)+(ch-'0');
            ch=getchar();
        }
        return a*x;
    }
    const int N=5e5;
    int n,k,m,l,r=-1e9,ans;
    int val[N];
    bool check(int x)             //每行最大值减去最小值都要小于等于x 
    {
        int tot=0;                //我们能匹配到tot行了 
        for(int i=1;i+m-1<=k;i++)
        {
            if(val[i+m-1]-val[i]<=x) //中间的点都可以选在同一行,那么两头的分别就是最大最小值 
            {
                tot++;
                i=i+m-1;
            }
            if(tot==n) return 1;
        }
        if(tot<n) return 0;
        else return 1;
    }
    int main()
    {
        freopen("a.in","r",stdin);
        freopen("a.out","w",stdout);
        k=read();n=read();m=read();
        for(int i=1;i<=k;i++)
        {
            val[i]=read();
            r=max(r,val[i]); 
        }
        sort(val+1,val+1+k);
        l=0;
        while(l<r)
        {
            int mid=(l+r)>>1;
            if(check(mid)) r=mid,ans=mid;
            else l=mid+1;
        }
        printf("%d",ans);
        return 0;
    }

    T2

    30 pts:

    枚举分割点的位置,然后一顿瞎搞,O ( n ! ) ;

    60 pts:

    我们完全可以用 dp 来做(然后我就写炸了。。。)

    我们先预处理出每一段的或和, 用 sum [ i ][ j ] 存起来;

    f [ i ][ j ] 表示考虑了前 i 个数,分成 j 段的最大和 。

    考虑转移:

    f [ i ][ j ] = max ( f [ i ][ j ] , f [ l ][ j-1 ] + sum [ l+1 ][ i ];

    100 pts:

    我们考虑去优化 dp;

    我们发现两个数组具有单调性: 

    f [ l ][ j-1 ] 随着 l 的增加递增;

    sum [ l+1 ][ i ] 随着 l 的增加而递减;

    对于固定的 i,sum [ k+1 ][ j ] 的取值最多只有 32 种,而且 f 数组的值单调递增。预处理出转移点即可。

    时间复杂度 O ( n * k * log C ),C 为 v [ i ] 的上界

    100+ pts:

    时间复杂度 O ( n k ) ? O ( n log C ) ? 欢迎补充

    B_extended:将最大值改为最小值,怎么做?

    T3

        

     

    令 n = 2, C ( x , y ) 为在 x 个物品中选择 y 个不同的物品的方案数,树的最底层为 0 。

    30 pts:

    k <= 3,也就是说 n <=8 ,我们可以直接枚举 n 的全排列,把所有的情况加起来!

    60 pts:

    k <= 10,n <= 1024;

    求出所有情况的魔法值;

    考虑:两个点 x 和点 y 在第 d 层对答案的贡献;

    算清楚 x 和 y 在第 d 层相遇了多少次:Σ(v [ x ] * v [ y ] * times [ x ][ y ][ d ]);

    不妨考虑 2d - 1 个编号小于 y 的;

    这才 60 pts 就已经晕了,那么接下来讲 100pts 的:

    更改求和顺序,改为 2 * 2k! * 2k! * ( n - 2k+1 ) ! * C ( y-1 , 2- 1 ) * v [ y ] * C ( x - 1 - 2,  2^k-1) * v [ x ] ,枚举 k 和 y,我们知道 x >= max ( 2k+1 ,  y+1 ),先对 C ( x - 1 - 2,  2- 1 ) * v [ x ] 求后缀和即可。时间复杂度 O ( n * k );

     我们可以预处理:

    数论

     

    POJ 3735  Training little cats

    有 n 只猫,有三种操作:

    1、让第 i 只猫花生数 +1;
    2、吃光第 i 只猫拥有的花生;
    3、交换两只猫拥有的花生;
    给出一个长度为 k 的操作序列,求将这个操作序列循环 m 次之后,每只猫拥有的花生数量; 

    题解:

    在线性代数中,矩阵乘法可以表示向量的变换;

    将 n 只猫拥有的花生数量看成一个列向量;

    操作1:向量的平移变换;

    操作2:向量的投影变换;

    操作3:向量的旋转变换;

    找出每种操作对应的矩阵,依次做乘法即可;

    考虑到 n 的范围太大, O ( n ) 的算法不大行,我们要想 log n 的算法;

    自然想到了快速幂,我们用矩阵快速幂试试:

    考虑到 k 是在不断变化的,但是最多就 18 种,所以我们做 18 次矩阵快速幂就好了,总时间复杂度 O(18 * 33 * log n);

     

    欧几里得算法

    给定数 a 和数 b,求两个数的最大公约数;

    欧几里得算法(辗转相除法)

    gcd(a , b)= gcd(b , a%b);

    时间复杂度 O(log C);

    扩展欧几里得的算法

    求 ax + by = gcd(a,b)的一组整数解;

    或者判断 ax + by = k 是否有解;

    P1516 青蛙的约会

    题解:

    最小公倍数

     判断素数

    给定一个数 n,判断 n 是否为素数;

    筛素数

    给定一个n,判断 1—n 这 n 个数哪些是素数;

    N ≤ 106

    用朴素的方法一个一个判断不可行;

    只要一个数是某个质因数的 k 倍(k > 1),那么这个数就是合数;

    数组 flag 表示每个数是不是素数;

    如果一个数是素数,则这个数的倍数都是合数;

    算法复杂度估计:O ( n ) – O ( n log n );

    实际算法复杂度:O ( n loglog n );

    线性筛素数 

    普通筛素数的方法里,一个合数有可能会被重复筛去多次,因此效率不高;

    改进思想:尽量少重复筛素数;

    先贴上代码:

    为什么时间复杂度是 O ( n )?

    需要证明的东西:

    1、我们不会重复删掉一个合数;

    2、1~n 的合数都会被我们找出来;

     

    对于一个合数,肯定能表示成:一个数 * 一个小素数 的形式;

    因此所有的素数都能被我们筛出来;

    算法正确性可以证明;

    欧拉函数 

    欧拉函数:给定数 n,求小于等于 n 中与 n 互素的数的个数;

    等价于求 φ ( n );

    等价于求 n 的质因数分解;

    O ( √n );

    线性求欧拉函数

    给定一个数 n,要求 1-n 中所有数的欧拉函数;

    利用欧拉函数的积性:

    性质5:若 a、b 互质,则 φ ( a * b ) = φ ( a ) * φ ( b );

    a % b == 0 , φ ( a * b ) =φ ( a ) * b;

    费马小定理&欧拉定理

    逆元

    求逆元

    线性求逆元

    给定素数 p,求 1~p-1 中每个数在模 p 意义下的的逆元;

    BZOJ 2705 [SDOI2012] Longge的问题

    题目描述:Longge 的数学成绩非常好,并且他非常乐于挑战高难度的数学问题。现在问题来了:给定一个整数 N,你需要求出 ∑ gcd ( i , N ) ( 1 <= i <= N )。

    对于 60% 的数据,0< N <= 216 

    对于 100% 的数据,0 < N <= 232 

    题解:

    组合数 

    杨辉三角 

    二项式定理

    同余

    组合数取模:问题一

    题解:

    问题二

    题解: 

    问题三

    题解: 

    P 为合数,对于 0~(p-1),不是每个数都有 ( mod p ) 意义下的逆元;

    但是 n , m , p 都不大,考虑组合数的计算式的变形:

    问题四

    Lucas定理

     

    问题五 

    题解: 

    Lucas 定理 + 中国剩余定理;

    用 Lucas 定理后得到 k 个同余方程;

    怎样合并这 k 个同余方程是我们要解决的问题;

    中国剩余定理

    问题六 

    题解: 

    扩展卢卡斯定理; 

    [ZJOI2010]排列计数

    题解:

    对于一个 n 个元素的二叉堆,其左右儿子为根的对,大小是确定的;

    那么设左儿子的对大小为 l,右儿子的对的大小为 r,r = n - l - 1;

    为什么是 n - l - 1 呢?因为根也占一个元素;

    设 f [ i ] 表示 i 个元素组成的二叉堆的方案数;

    f [ n ] = C ( n-1 , l ) * f [ l ] * f [ r ] % p;

    若 p 很大,直接做;

    若 p 很小,1……p-1 的数是有逆元的;

    Lucas 定理,注意 p 必须是素数才能用;

    高斯消元

    解方程组;

    思想:利用加减消元法解方程;

    [HNOI2013]游走

    题解:

    期望走过的次数越大的边,编号越小 ==> 要求总分的期望最小;

    每个点的期望经过次数 ==> 求每条边的期望经过次数;

    一条边 编号为 k,端点为(x,y)

    p [ x ] 表示第 x 个点的期望经过次数;

    du [ x ] 表示与 x 相连的点数;

    那么低 k 条边的期望经过次数就是 p [ x ] * 1 / du [ x ] + p [ y ] * 1 / du [ y ];

    假设 x 号店与 t1 , t, t3 ,..... tk 相连;

    p [ 1 ] = p [ t1 ] / du [ t] + p [ t2 ] / du [ t2 ] + ...... + p [ tk ] / du [ tk ] + 1; 

    p [ x ] = p [ t1 ] / du [ t] + p [ t2 ] / du [ t2 ] + ...... + p [ tk ] / du [ tk ] ;  

    有了 n 个方程,n 个未知数,高斯消元;

    大步小步算法 

    课后作业:Bzoj2242:[SDOI2011]计算器

     

    老师的手稿证明过程: 

     

    NOIp 会涉及到的数学相关知识:

    1. 快速幂;

    2. 矩阵乘法;

    3. GCD / exGCD;

    4. 筛素数 / 素数判断;

    5. 欧拉函数 / 质因数分解;

    6. 逆元;

    7. 组合数问题 / 卢卡斯定理 ;

    8. 中国剩余定理;

    9. 高斯消元;

  • 相关阅读:
    黄聪:DEDECMS织梦 真正的随机文章的调用方法
    黄聪:Apache 301重定向中RewriteCond规则参数介绍(转)
    [阅读笔记]fsnotify源码阅读
    [阅读笔记]Go语言并发之美
    NUMA的取舍
    Web服务器那些招
    PHP执行批量mysql语句
    gotour练习解答
    mysql多个TimeStamp设置
    mysql那些招
  • 原文地址:https://www.cnblogs.com/xcg123/p/11624969.html
Copyright © 2020-2023  润新知