• 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. 高斯消元;

  • 相关阅读:
    WHERE col1=val1 AND col2=val2;index exists on col1 and col2, the appropriate rows can be fetched directly
    MySQL 交集 实现方法
    MBProgressHUD的使用
    Xcode4 使用 Organizer 分析 Crash logs(转)
    SimpleXML 使用详细例子
    PHP的XML Parser(转)
    iPhone,iPhone4,iPad程序启动画面的总结 (转)
    Pop3得到的Email 信件格式介绍
    yii总结
    隐藏Tabbar的一些方法
  • 原文地址:https://www.cnblogs.com/xcg123/p/11624969.html
Copyright © 2020-2023  润新知