TopCoder 603 div1 & div2

div2

250pts MiddleCode


题意:s串长度为奇数时,将中间字符取掉并添加到t末尾;长度为偶数时,将中间两个较小的字符取掉并添加到末尾。

分析:直接做,学习了一下substr(s, pos, len)返回s中从pos开始的长度为len的字串。

代码:

class MiddleCode {
public:
    void Remove(string &s, int pos) {
        int len = s.size();
        string t = "";
        if(pos < 0 || pos >= len) return;
        for(int i = 0; i < len; i++)
            if(i != pos) t += s[i];
        s = t;
    }
    string encode(string s) {
        int n = s.size(), m = n;
        string ans = "";
        for(int i = 0; i < n; i++) {
            if((n-i)&1) {
                ans += s[(n-i)/2];
                Remove(s, (n-i)/2);
            } else {
                if(s[(n-i)/2] < s[(n-i)/2-1]) {
                    ans += s[(n-i)/2];
                    Remove(s, (n-i)/2);
                } else {
                    ans += s[(n-i)/2-1];
                    Remove(s, (n-i)/2-1);
                }
            }
        }
        return ans;
    }
};


500pts SplitIntoPairs

题意:将N个数(偶数)分成N/2组,使得两个数的乘积>=X的组数尽量多,X < 0。

分析:X < 0,所以只有当每组的两个数A,B一正一负时才有可能比X小,将N个数分成负数和非负数两组,如果负数有偶数个,那么结果就是N/2组,

因为负数,正数可以分别两两配对。当为奇数的时候,负数和正数两两配对正好剩下一个,且绝对值应该尽量小,判断两数之积和X的关系即可。

代码:

class SplitIntoPairs {
public:
    int makepairs(vector <int> A, int X) {
//       int n = sz(A);
       sort(A.begin(), A.end());
       vector<int> B, C;
       for(int i = 0; i < sz(A); i++){
           if(A[i] >= 0) B.pb(A[i]);
           else C.pb(A[i]);
       }
       int n = sz(B), m = sz(C);
       if(n%2 == 0) return (n+m)/2;
       return (n+m)/2-(1LL*C[m-1]*B[0] < X);
    }
};




950pts GraphWalkWithProbabilities

题意:从一点出发,每一轮选择任意可达的点,该点有win[i], lose[i], 1-win[i]-lose[i]三个概率, 表示到达该点赢,输,继续的概率,从Start出发,

按照最优的走法,最后赢d的概率。

分析:从某一点x出发能够赢得概率和转移到相邻的点y,然后赢的概率有关,但是图中可能存在环,因此采用记忆化搜索的话,

会存在相互依赖关系构成环的情况,转移到一个点y要能够继续进行的话概率为-win[y]-lose[y],那么可以设从y出发,最多进行steps轮,最后能够赢得概率。

这样转移dp[node][steps] = max{ win[to] + (1-win[to]-lose[to]) * dp[to][steps-1] };

steps上界设为3000左右即可,因为1-win[to]-lose[to]最大0.99, 最多3000轮,最终赢得概率应该是能满足题目精度要求的。

代码:

const int maxn = 3000 + 10;
double dp[55][maxn];

class GraphWalkWithProbabilities {
public:
    vector<int> win, lose;
    vector<int> g[55];

    double dfs(int node, int steps) {
        double &res = dp[node][steps];
        if(!(res < 0)) return res;
        res = 0;
        for(int to: g[node])
            res = max(res, win[to]/100.0 + (100-win[to]-lose[to])/100.0*dfs(to, steps-1));
        return res;
    }
    double findprob(vector <string> graph, vector <int> winprob, vector <int> loseprob, int Start) {
        for(int i = 0; i < 55; i++) for(int j = 0; j < maxn; j++)
            dp[i][j] = -1.0;
//        bug(1)
        for(int i = 0; i < 55; i++) dp[i][0] = 0.0;
        win = winprob;
        lose = loseprob;
//        bug(1)
        int n = sz(graph);
        for(int i = 0; i < n; i++){
            for(int j = 0; j < n; j++)
                if(graph[i][j] == '1')
                g[i].pb(j);
        }
        dfs(Start, maxn-1);
        return dp[Start][maxn-1];
    }


};


div1

250pts MaxMinTreeGame


题意:给定一棵N(2 <= N <= 50)的树,两人轮流进行游戏,每次可以删除一条边,然后选择保留其中一棵子树,直到仅剩下一个结点,游戏结束,每个结点都有一个权值,

A想要使得 最后结果尽量大,B想要使得结果尽量小,两人均按照最优方式进行,A先手,求A最终得到的最大值。

分析:所有度数为1的点的中权值最大值(M)即为结果,首先要证明所能获得的最大值不会超过M,因为N>=2的树中度数为1 的结点至少2个,

所以不论A先手时如何操作,剩下的树中,一定会保留下这些结点中的一个,B操作时选取即可,然后证明A先手能够保留权值最大的结点,这个是显然的。

代码:

const int maxn = 55;
int du[maxn];

class MaxMinTreeGame {
    public:
    int findend(vector <int> edges, vector <int> costs) {
        memset(du, 0, sizeof du);
        int n = sz(edges) + 1;
        for(int i = 0; i < sz(edges); i++)
            du[i+1]++, du[edges[i]]++;
        int ans = 0;
        for(int i = 0; i < n; i++)
            if(du[i] == 1)
            ans = max(ans, costs[i]);
        return ans;
    }


};





500pts PairsOfStrings

题意:给定字符集合为前k个小写字母,定义字符集合上的长度为n的A,B字符串,若存在定义在集合上的C使得A+C=C+B,那么(A,B)记为一对,

现在问(n,k)能够确定的数目,结果MOD (int)1e9 + 7。

分析:首先应该知道B应该是A旋转后的字符串,定义字符串的最小周期长度,A = d^n/d,表示A由n/d个长度为d字符串(记为s)链接构成,A = s + s + ... s,

且不存在更小的长度为d' < d的字符串s',s'重复n/d'之后能够得到A。这样A旋转操作能够得到的不同字符串就为d。那么对于本题需要知道最小周期长度的为d字符串有多少个(num),

最终结果就是所有的d*num之和。显然d应该是n的因子,可能的情况是k^d种,然后这里会有存在重复的情况,例如n = 8, d = 4时,结果k^4中,会包含d = 2中情况,

s = aaaa,A = s+s = aaaaaaaa,显然A可以看做周期长度更小的s' = aa,A = s' + s' + s' + s',所以要把d的因子d'所对应的情况排除。n <= (int)1e9,因子最多1300+个,最后复杂度应该是

O(1300*1300)。

代码:

const int M = 1000000007;

class PairsOfStrings {
public:
    int num[1500];
    int powmod(LL a, LL b, LL c) {
        LL res = 1;
        while(b) {
            if(b&1) res = res*a%c;
            a = a*a%c;
            b >>= 1;
        }
        return res;
    }
    void addIt(int &x, int y) {
        x = (x+y)%M;
        if(x < 0) x += M;
    }
    void getDivisors(int n, vector<int> &div) {
        div.clear();
        int m = (int)sqrt(n+.5);
        for(int i = 1; i <= m; i++)
            if(n%i == 0) {
                div.pb(i);
                if(n/i != i)
                    div.pb(n/i);
            }
        sort(div.begin(), div.end());
    }
    vector<int> div;
    int getNumber(int n, int k) {

        getDivisors(n, div);
        int ans = 0;
        for(int i = 0; i < sz(div); i++) {
            int x = div[i];
            num[i] = powmod(k, x, M);
            for(int j = 0; j < i; j++) {
                int y = div[j];
                if(x%y == 0) {
                    addIt(num[i], -num[j]);
                }
            }
            addIt(ans, 1LL*num[i]*x%M);
        }
        return ans;
    }


};




1000pts SumOfArrays

题意:A,B两个数组长度 n <= 100000,A[i],B[i] < 100000,将A,B中的数排列后,使得C[i] = A[i]+B[i]使得C[i]中出现过的数Y出现次数最大。

分析:又是FFT的应用,做法很奇特!分别统计A[i]和B[i]中数出线次数即cntA[A[i]],cntB[B[i]],然后考虑

解法的关键之处就是这里的转化,考虑min(cntA[p],cntB[q]) >= k,那么C[p+q]为(p,q,k)的组数,针对k分别考虑:

k >= 10,显然cntA[p],cntB[q] >= k的p和去不超过(int)1e4,暴力统计C[p+q]的复杂度不会超过(int)1e8,当然实际复杂度可能更低。

k < 10,

z[] = full of zeros
For p = 0 ... 100000 {
For each q = 0 ... 100000 {
z[p + q] = z[p + q] + (x[p] * y[q])
}
}
for i = 0 ... 200000 {
C[i] = C[i] + z[i]
}
这里和大数的乘法十分相似,设立两个数组x[],y[],当x[p] = cntA[p] >= k,y[q] = cntB[q] >= k,剩下的部分利用FFT求出,z[p+q] += x[p]*y[q],由于FFT复杂度为O(MAX*log(MAX)),9次FFT是能够满足效率要求的。

代码:

const int maxn = (int)2e5 + 10;
const int LOW = 10;

int cntA[maxn], cntB[maxn];
int A[maxn], B[maxn], C[maxn];
bitset<maxn> a, b;

struct Complex {
    double x, y;
    Complex() {}
    Complex(double x, double y):x(x), y(y) {}
};
Complex operator + (const Complex &a, const Complex &b) {
    Complex c;
    c.x = a.x+b.x;
    c.y = a.y+b.y;
    return c;
}
Complex operator - (const Complex &a, const Complex &b) {
    Complex c;
    c.x = a.x-b.x;
    c.y = a.y-b.y;
    return c;
}
Complex operator * (const Complex &a, const Complex &b) {
    Complex c;
    c.x = a.x*b.x-a.y*b.y;
    c.y = a.x*b.y+a.y*b.x;
    return c;
}

inline void FFT(vector<Complex> &a, bool inverse) {
    int n = a.size();
    for(int i = 0, j = 0; i < n; i++) {
        if(j > i)
            swap(a[i], a[j]);
        int k = n;
        while(j & (k>>=1)) j &= ~k;
        j |= k;
    }
    double PI = inverse ? -pi : pi;
    for(int step = 2; step <= n; step <<= 1) {
        double alpha = 2*PI/step;
        Complex wn(cos(alpha), sin(alpha));
        for(int k = 0; k < n; k += step) {
            Complex w(1, 0);
            for(int Ek = k; Ek < k+step/2; Ek++) {
                int Ok = Ek + step/2;
                Complex u = a[Ek];


                Complex t = a[Ok]*w;
                a[Ok] = u-t;
                a[Ek] = u+t;
                w = w*wn;
            }
        }
    }
    if(inverse)
        for(int i = 0; i < n; i++)
            a[i].x = (a[i].x/n);
}
vector<int> operator * (const bitset<maxn> &v1, const bitset<maxn> &v2) {
    int S1 = v1.size(), S2 = v2.size();
    int S = 2;
    while(S < S1+S2) S <<= 1;
    vector<Complex> a(S), b(S);
    for(int i = 0; i < S; i++)
        a[i].x = a[i].y = b[i].x = b[i].y = 0.0;
    for(int i = 0; i < S1; i++)
        a[i].x = v1[i];
    for(int i = 0; i < S2; i++)
        b[i].x = v2[i];
    FFT(a, false);
    FFT(b, false);
    for(int i = 0; i < S; i++)
        a[i] = a[i] * b[i];
    FFT(a, true);
    vector<int> res(maxn, 0);
    for(int i = 0; i < maxn; i++)
        res[i] = round(a[i].x);
    return res;
}

class SumOfArrays {
public:
    void gen(int A[], vector<int> seed, int n) {
        A[0] = seed[0];
        A[1] = seed[1];
        for(int i = 2; i < n; i++)
            A[i] = (1LL * A[i-1] * seed[2] + 1LL* A[i-2] * seed[3] + seed[4]) % seed[5];
//        for(int i = 0; i < n; i++)
//            printf("%d ", A[i]);
//        puts("");
    }
    char ans[100];

    string findbestpair(int n, vector <int> Aseed, vector <int> Bseed) {
        gen(A, Aseed, n);
        gen(B, Bseed, n);
//        memset(cntA, 0, sizeof cntA);
        memset(cntB, 0, sizeof cntB);
        memset(C, 0, sizeof C);

        for(int i = 0; i < n; i++) {
            cntA[A[i]]++;
            cntB[B[i]]++;
        }

        vector<int> bigA, bigB;
        for(int i = 0; i < maxn; i++) {
            if(cntA[i] >= LOW)
                bigA.pb(i);
            if(cntB[i] >= LOW)
                bigB.pb(i);
        }

        for(int p: bigA) for(int q: bigB) {
                C[p+q] += min(cntA[p], cntB[q]) - LOW + 1;
            }
        vector<int> c(maxn);
        for(int k = 1; k < 10; k++) {
            a.reset();
            b.reset();
            for(int i = 0; i < maxn; i++) {
                if(cntA[i] >= k)
                    a[i] = 1;
                if(cntB[i] >= k)
                    b[i] = 1;
            }

            c = a*b;
            for(int i = 0; i < maxn; i++)
                C[i] += c[i];
//            for(int i = 1; i <= 4; i++)
//            cout << C[i] << ' ';
//            cout << endl;
        }

        int X = -1, Y = 0;
        for(int i = 0; i < maxn; i++)
            if(C[i] >= X) {
                X = C[i];
                Y = i;
            }

        sprintf(ans, "%d %d", X, Y);
//        TL
        return ans;
    }


};


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值