Codeforces #201 div1

2015-06-27 14:19:45

传送门

总结:虚拟参赛的一场,题目都很有质量!比的不是很好,rank540+

  赛后补到第三题。

 

A题:数学

  题意:给出 n (n<=100)个互不相同的数的集合,Alice 和 Bob 轮流进行操作,每次操作取出任意两个数,将他们差的绝对值加入集合(注意,这个绝对值一定不能已经在集合中),不能操作的人输。

  思路:仔细思考会发现大部分的集合操作到最后都是 1,2,3... maxval,maxval是指原来集合中的最大值。但是有那么几种,比如 3 9 12 操作到最后是 3 6 9 12,其实只要求出原来集合中所有数的gcd,操作到最后集合一定是:gcd,2*gcd,....,maxval,本质其实就是数的拆分的过程,知道这个结论后就可以做咯。

#include <cstdio>
#include <ctime>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;

#define getmid(l,r) ((l) + ((r) - (l)) / 2)
#define MP(a,b) make_pair(a,b)
#define PB(a) push_back(a)

typedef long long ll;
typedef pair<int,int> pii;
const double eps = 1e-8;
const int INF = (1 << 30) - 1;
const int MAXN = 110;

int n,A[MAXN];

int main(){
    int tmax = 0,G;
    scanf("%d",&n);
    for(int i = 1; i <= n; ++i){
        scanf("%d",&A[i]);
        tmax = max(tmax,A[i]);
        if(i == 1) G = A[1];
        else G = __gcd(G,A[i]);
    }
    int cnt = tmax / G - n;
    if(cnt & 1) printf("Alice\n");
    else printf("Bob\n");
    return 0;
}
View Code

 

B题:KMP,DP

  题意:给出三个串 s1,s2,s3,求出 s1、s2 的最长公共子序列(与子串不同,序列意味着不一定连续),且 s3 不是这个子序列的子串。注意三个串的长度均小于等于 100。

  思路:比较容易想到三维 DP,dp[i][j][k]表示考虑 s1 前 i 个字符,s2 前 j 个字符,且已经匹配了 s3 串前 k 个字符的最长子序列长度。

  因为要输出最长子序列,所以要记录一下路径,这些都不是问题,关键在于与 s3 失配之后并不是简单地从头重新匹配,而是应该通过KMP求出的失配数组返回到前一个应该考虑的位置 P[k]。所以 dp 时如果 s1[i] == s2[j] != s3[k] 时,应该用类似 KMP 的过程求出上一个匹配位置 k' 再转移。

#include <cstdio>
#include <ctime>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;

#define getmid(l,r) ((l) + ((r) - (l)) / 2)
#define MP(a,b) make_pair(a,b)
#define PB(a) push_back(a)

typedef long long ll;
typedef pair<int,int> pii;
const double eps = 1e-8;
const int INF = (1 << 30) - 1;

int dp[102][102][102];
int pre[102][102][102];
char s1[102],s2[102],v[102];
vector<char> g;
int P[102];

void Get_P(){
    memset(P,0,sizeof(P));
    int j = 0;
    int len = strlen(v + 1);
    for(int i = 2; i <= len; ++i){
        while(j > 0 && v[j + 1] != v[i]) j = P[j];
        if(v[j + 1] == v[i]) j++;
        P[i] = j;
    }
}

void Print(int a,int b,int c){
    if(pre[a][b][c] == -1)
        return;
    if(pre[a][b][c] == -10){
        g.PB(s1[a]);
        Print(a - 1,b - 1,c - 1);
    }
    else if(pre[a][b][c] >= 0){
        g.PB(s1[a]);
        Print(a - 1,b - 1,pre[a][b][c]);
    }
    else if(pre[a][b][c] == -20){
        Print(a - 1,b,c);
    }
    else if(pre[a][b][c] == -30){
        Print(a,b - 1,c);
    }
}

int main(){
    memset(pre,-1,sizeof(pre));
    scanf("%s%s%s",s1 + 1,s2 + 1,v + 1);
    Get_P();
    int l1 = strlen(s1 + 1);
    int l2 = strlen(s2 + 1);
    int lv = strlen(v + 1);
    int ans = 0,ansi,ansj,ansk;
    for(int i = 0; i <= l1; ++i){
        for(int j = 0; j <= l2; ++j){
            for(int k = 0; k < lv; ++k){
                if(s1[i + 1] == s2[j + 1]){
                    if(k + 1 < lv && s1[i + 1] == v[k + 1]){
                        if(dp[i + 1][j + 1][k + 1] < dp[i][j][k] + 1){
                            dp[i + 1][j + 1][k + 1] = dp[i][j][k] + 1;
                            pre[i + 1][j + 1][k + 1] = -10;
                        }
                    }
                    if(s1[i + 1] != v[k + 1]){
                        int tk = k;
                        while(tk && s1[i + 1] != v[tk + 1]){
                            tk = P[tk];
                        }
                        if(s1[i + 1] == v[tk + 1]) ++tk;
                        if(dp[i + 1][j + 1][tk] < dp[i][j][k] + 1){
                            dp[i + 1][j + 1][tk] = dp[i][j][k] + 1;
                            pre[i + 1][j + 1][tk] = k;
                        }
                    }
                }
                if(dp[i + 1][j][k] < dp[i][j][k]){
                    dp[i + 1][j][k] = dp[i][j][k];
                    pre[i + 1][j][k] = -20;
                }
                if(dp[i][j + 1][k] < dp[i][j][k]){
                    dp[i][j + 1][k] = dp[i][j][k];
                    pre[i][j + 1][k] = -30;
                }
                //printf("dp[%d][%d][%d] :%d\n",i,j,k,dp[i][j][k]);
                if(dp[i][j][k] > ans){
                    ans = dp[i][j][k];
                    ansi = i , ansj = j , ansk = k;
                }
            }
        }
    }
    //printf("%d %d %d %d\n",ans,ansi,ansj,ansk);
    if(ans == 0){
        printf("0\n");
        return 0;
    }
    Print(ansi,ansj,ansk);
    for(int i = g.size() - 1; i >= 0; --i) printf("%c",g[i]);
    puts("");
    return 0;
}
View Code

 

C题:贪心

  题意:给出 a 和 b (0  ≤ b ≤  a ≤ 10^9, a - b ≤ 10^6),再给出最多 10^5 个数:x1~xn (2 <= xi <= 10^9),我们要把 a 变成 b,每次可以减去 1,或者减去 当前值 % xi,问最少步数。

  思路:乍一看没啥思路,虽然有人用暴力乱搞直接过掉了... 但是也有其他的方法(ORZ 杰哥!)

  首先我们发现操作的本质就是变成一个 xi 的倍数(在 xi 集合中加入 1,这样减1也符合这个本质了),所以 b 也就必定是某个 xi 的倍数,我们倒着考虑,从 b 加到 a 。首先看 b 是 xi 集合中哪些数的倍数,比如是 xk1 , xk2 , ... , xkm。那么 [b + 1, b + max(xki) - 1] 这些数只用一步操作就能到达 b 了,我们令这个区间为第一区间,我们将刚刚对 b 的操作命为找 b 的最大可达点。然后我们再尝试找第二区间(也就是用两步操作能到达 b 的区间),其实就是找用一步操作能到达第一区间的数,那么需要对第一区间内的所有数找一次最大可达点,然后再求最大值,所以第二区间:[b + max(xki) , max(最大可达点)]。

  于是我们就获得了一个将 b 到 a 之间的区域进行划分的做法。第 i + 1 个区间的右端点可以由第 i 个区间内所有数的最大可达点的最大值求得。

  显然我们需要一种很快速的方法来求出最大可达点,考虑用筛法,维护一个 w[] 数组,对于所有 xi,考虑其所有的倍数来更新其每个倍数的最大可达点。

  w[k * xi] = max(w[k * xi] , k * xi + xi - 1),然后就可以用一遍巧妙的循环来划分区间从而求出最小步数了,具体见程序。

  注意点:xi 需要去重~

#include <cstdio>
#include <ctime>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <string>
#include <ctime>
#include <iostream>
#include <algorithm>
using namespace std;

#define getmid(l,r) ((l) + ((r) - (l)) / 2)
#define MP(a,b) make_pair(a,b)
#define PB(a) push_back(a)

typedef long long ll;
typedef pair<int,int> pii;
const double eps = 1e-8;
const int INF = (1 << 30) - 1;
const int MAXN = 100010;

int n;
int A,B,x[MAXN];
int mp[1000010];

int main(){
    scanf("%d",&n);
    for(int i = 1; i <= n; ++i)
        scanf("%d",x + i);
    sort(x + 1,x + n + 1);
    scanf("%d%d",&A,&B);
    for(int i = B; i <= A; ++i) mp[i - B] = i + 1;
    for(int i = 1; i <= n; ++i){
        if(i > 1 && x[i] == x[i - 1]) continue;
        int p = B / x[i] * x[i];
        if(p < B) p += x[i];
        while(p < A){
            mp[p - B] = max(mp[p - B],p + x[i] - 1);
            p += x[i];
        }
    }
    int cnt = 0,p = B,nxt_tp = -1,tp = mp[p - B];
    while(p < A){
        p++;
        nxt_tp = max(nxt_tp,mp[p - B]);
        if(p == tp || p == A){
            cnt++;
            tp = nxt_tp;
        }
    }
    printf("%d\n",cnt);
    return 0;
}
View Code

 

 

 

posted @ 2015-06-27 15:05  Naturain  阅读(212)  评论(0编辑  收藏  举报