zoukankan      html  css  js  c++  java
  • 2013512 CF 183 总结

    还是很弱啊,发现确实要多做CF, 不单单训练思维,而且还有代码。还能多参考大神的思路与代码。

    Div 2.

    A题, 给定 n <= 5000, 求满足 1<=a<=b<=c<=n, 的直角三角形数量.

    解法, 数据量不大,可以暴力枚举 a, b, 然后求满足的 c

    View Code
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    typedef long long LL;
    const int N = 10000;
    
    int main(){
    //    memset(ans, 0 , sizeof(ans));    
        int n; long long ans = 0;
        scanf("%d", &n);
        for(int a = 1; a <= n; a++)
            for(int b = a; b <= n; b++ ){
                int cc = a*a+b*b, c = (int)sqrt(cc);
                if( c*c == cc ){
                    if( (c<=n) &&  (c<a+b) ){
                        ans++;    
                    }    
                }
            }
        printf("%lld\n", ans );
        return 0;
    }

    B题, 给两个日期形式如 yyyy:mm:dd, 求之间的总天数,包含开始,不包含结束那天。

    解法,模拟,看到 Python or Ruby  4,5行的代码,真心跪了。

      我的写法是, 写一个函数 g(), 用来计算同一年下,从日期A到日期B所需天数. 那么对于给定日期A到日期B,首先从A算到下

    一年起始,调用g()即可,然后再整年整年算到B年去, 这时候再调用下 g().   注意 日期A,B大小没说明.

    View Code
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    using namespace std;
    int a[2][13] = {
    {0,31,28,31,30,31, 30,31,31,30,31, 30,31},
    {0,31,29,31,30,31, 30,31,31,30,31, 30,31}
    };
    int b[2] = {365,366};
    bool legal(int y){
        if( (y%400==0) || ((y%100!=0)&&(y%4==0)) ) return true;
        return false;
    }
    
    int mon( int y1,int y2, int m1,int m2,int d1,int d2){
        int cnt = 0;
        if( m1 == m2 ){
            return d2 - d1;    
        }    
        else{
            int x = legal(y1);    
            cnt = a[x][m1] - d1 +1;    
            int m = m1+1;
            while( m < m2 ) cnt += a[x][m++];
            cnt += d2-1;
            return cnt;    
        }
    }
    int solve(int y1,int m1,int d1, int y2,int m2,int d2){
        if( (y1>y2)||( (y1==y2)&&(m1>m2) )||( (y1==y2)&&(m1>m2)&&(d1>d2) ) ){
            swap(y1,y2); swap(m1,m2); swap(d1,d2);    
        }
        int ans = 0;    
        if( y1 == y2 ){
            return mon( y1,y2,m1,m2,d1,d2);    
        }
        else{
            int x = legal(y1);    
            ans = mon( y1, y1, m1, 12, d1, 31 )+1; // y1+1, 1, 1     
            int y = y1+1;
            while( y < y2 ){
                ans += b[ legal(y) ];
                y++;
            }    
            int t = mon( y2,y2,1,m2,1,d2 );    
            ans += t;    
            return ans;    
        }
    }
    int main(){
        int y1,m1,d1;
        int y2,m2,d2;
        char s[100];
        scanf("%s", s);
        sscanf( s, "%d:%d:%d", &y1,&m1,&d1);
        scanf("%s", s);
        sscanf( s, "%d:%d:%d", &y2,&m2,&d2);
        printf("%d\n", solve( y1,m1,d1, y2,m2,d2) );
        return 0;
    }

    C题,  给一个n, 其全排列( 0,1,..,n-1 ), 求其中三个排列 a, b, c, 满足   ai+bi = ci ( mod n ), 若不存在则输出 -1.

    解法: 构造,  抽象成 n边形,顶点序列为 c1,c2,c3,...,cn .  当n为奇数时,则ai顶点出发,与 a(i+2) 连边,然后 a(i+2)与a(i+4)连边,如此反复,

    即可不重复的将所有顶点连成一条线上. 差值为2,意味这我们每次增长2, 那么就有 ci = (i+i)%n.

       对于 n为偶数的情况下, 则不存在. 因为若要将所有顶点通过一条直线连接,  则每次顶点编号+1, 那么

    若 a = { 0,1 ,2, ..., n-1 },  因为每次 ai 编号+1了, 则要保持 ci连成直线 ,则意味着 bi需要保持不变. 因为 bi = {0,1,..,n-1},不存在这样的b.

    所以不合法.

    View Code
    #include<cstdio>
    int main(){
        int n;
        scanf("%d", &n);    
        if( n&1 ){
            for(int i = 0; i < n; i++) printf(i==0?"%d":" %d", i ); puts("");
            for(int i = 0; i < n; i++) printf(i==0?"%d":" %d", i ); puts("");
            for(int i = 0; i < n; i++) printf(i==0?"%d":" %d",(i+i)%n); puts("");
        }
        else printf("-1\n");    
        return 0;
    }

    D题,  二维坐标系第一象限中,给定一个 (0,0)与(n,m)构成的矩形, 和一个 (x,y), 与一个(a,b). 求一个最大的子矩形,包含(x,y),并且,

    左下点(x1,y1),与右上角(x2,y2),满足 (x2-x1)/(y2-y1) = a/b , 所有数都要为整数.  若子矩阵存在多个,输出矩阵中心点 与 (x,y)曼哈顿距离最短的.

    若还是存在多个,则输出 (x1,y1,x2,y2) 字典序小的,(其实就是偏左边的). 

    解法: 数学构造. 

      首先要满足子矩阵最大, 并且 (x2-x1)/(y2-1) = a/b ,  令 d = gcd( a, b ), a1 = a/d, b1 = b/d. 那么 (x2-x1)/(y2-y1) = a1/b1, 等价于

    k*a1 /  k*b1 = a1 / b1,  换句话说,就是 x轴有 k段a1, y轴有 k段b1, 找到一个最大的整数 k, 即是满足要求的最大子矩阵大小. 很容易得到 k = min( n/a1, m/b1 )

      由上面分析,我们知道矩阵的长宽分别为 ( k*a1, k*b1 ).  最极端情况是右上角顶点是(n,m), 则左下角顶点是( n-k*a1, m-k*b1 ). 这是最靠右的方案.

      现在还要满足后续要求, 

      第一, 中心点距离(x,y),要尽可能小, 那么  矩阵的中心点为(x,y)时,必然最小,  则左下角顶点应该为  { x- (k*a1+1)/2, y - (k*b1+1)/2 }, 这里 (k*a1+1)/2的

    原因是, 若 k*a1 是偶数,肯定是取最靠左边那个, 若k*a1是奇数,则当前这一段的中点是 (k*a1+1)/2, 两种情况合并到一起就是 (k*a1+1)/2.  

      第二, 若选 (x,y)作为子矩形中点, 其左下角可能会超过 x轴,y轴,而为负数,这不合法.所以最小只能是 x1 = 0, y = 0.

      所以有   x1 = min(  n-k*a1, max( 0,x-(k*a1+1)/2)    ) , y1类似, 而 x2 = x1+k*a1, y = y1+k*b1. 即是最终结果.

    View Code
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    
    int n, m, x, y, a, b;
    
    int gcd(int a,int b){
        return b==0?a:gcd(b,a%b);
    }
    int main(){
        scanf("%d%d%d%d%d%d",&n,&m,&x,&y,&a,&b);
        int d = gcd(a,b);
        int a1 = a/d, b1 = b/d;
        int k = min( n/a1, m/b1 );
        int x1 = min( n-k*a1, max( 0, x - (k*a1+1)/2 ) );
        int y1 = min( m-k*b1, max( 0, y - (k*b1+1)/2 ) );
        printf("%d %d %d %d\n", x1,y1,x1+k*a1,y1+k*b1 );
        return 0;
    }

    E题, 给 n(n<=5000)个数, a1,a2,...,an, (ai <= 10^6 ), 问最多去掉 k (k<=4)个数的情况下,使得序列中不再存在 ai = aj (mod M),求最小的M.

    解法: 看了某大神的代码,学会的思路. 不过还是对时间复杂度不太自信~~~

      大致思路是这样的:  枚举m, (感觉有点凶残,虽说极端情况 m = 10^6 + 1 ),  统计 ai%m余数出现重复的次数,若小于等于k则输出M,.

      有个地方可以优化. 令 gap[x]  表示 ai - aj = x 出现的次数,  那么我们就能统计出  ai = aj+t*m 的形式的总数量. 设其为sum, 因为最多删除

    k个数, 则极端情况下影响 k+1个数,的数量为  k*(k+1)/2, 所以若 sum > k*(k+1)/2 直接可以判定此时不可能,跳过即可. 

      对于 sum > k*(k+1)/2 这里确实要分析下, 因为我们最多只能删除掉K个数,  假定我们有 k+1个数相互间影响(满足ai = aj+t*m ), 分别为 a_1, a_2, ..., a_k, a_k+1,

    则 产生的影响个数最多为 :  ( 假定 a_i, a_i+1, 序列非递减  )

      a_1 能 影响 a_2 , a_3 ,.. , a_k+1 ,    共 k 个

      a_2 能 影响 a_3, a_4 ,....., a_k+1,    共 k-1 个

      ....

      a_k 能影响 a_k+1,  共 1个.

      则 总数量为   k+(k-1) + ... + 1  =  k*(k+1)/2  ,  

      所以有 当 sum > k*(k+1)/2 时,  直接判定当前的m不合法.

    View Code
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    using namespace std;
    
    const int N = 1000010;
    
    int a[5010];
    bool b[N];
    int gap[N];
    
    int main(){
        int n, K, i, j, m;
        scanf("%d%d", &n, &K);
        for(int i = 0; i < n; i++) scanf("%d", &a[i]);
        sort( a, a+n );
        memset(b,0,sizeof(b));
        memset(gap,0,sizeof(gap));
        for(int i = 0; i < n; i++)
            for(int j = 0; j < i; j++)
                gap[ a[i]-a[j] ]++;
    
        for(m = 1;;m++){
            bool flag = true;    
            int cnt = 0, sum = 0;    
            for(i = m; i < N; i += m )  sum += gap[i];
            if( sum > (K*(K+1)/2) ) continue;
            for(i = 0;i < n; i++){
                int x = a[i]%m; 
                if( !b[x] ) b[x] = true;
                else{
                    if(++cnt > K){flag = false; break;}    
                }
            }    
            for(j = 0; j < i; j++) b[ a[j]%m ] = false;
            
            if( flag ){ printf("%d\n",m); return 0; }
        }
        return 0;
    }

      

       

  • 相关阅读:
    Linux-shell-算术运算{expr、bc、dc、(( ))和[ ]}
    [SHELL]:let 命令详解
    23-tcp协议——TIME_WAIT状态和FIN_WAIT2状态
    ethtool -p eth0 物理口一个灯在不停的闪烁
    PXE
    UID, EUID, SUID, FSUID
    echo $[1 + 2] shell中 $[] 在bash中同$(()),用于算术计算
    Cocos2d入门--3-- 向量的应用
    Cocos2d入门--2-- 三角函数的应用
    Cocos2d入门--1-- 初涉相关属性或代码
  • 原文地址:https://www.cnblogs.com/yefeng1627/p/3075512.html
Copyright © 2011-2022 走看看