2013-5-12 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;
}

 

  

 

 

   

posted @ 2013-05-13 12:58  yefeng1627  阅读(181)  评论(0编辑  收藏  举报

Launch CodeCogs Equation Editor