【log】近期刷题 - 2015.9 - 2015.10

来源:互联网 发布:m2数据 2016 编辑:程序博客网 时间:2024/05/16 23:36

2015 沈阳赛区网络赛

hdu 5456 Matches Puzzle Game

从低位到高位dp,每次枚举 B,C,维护借位,还有B,C是否到达最高位。
dp(剩余火柴数,是否需要借位,B是否到达最高位,C是否到达最高位)

const int N = 500;const int num[10] = {6, 2, 5, 5, 4, 5, 6, 3, 7, 6};LL dp[N+5][2][2][2], Mod;inline void add_mod(LL& x, LL y) {    x += y; if ( x >= Mod ) x -= Mod;}LL go(int n, int carry, int b, int c) {    if ( n == 0 ) {        if ( carry ) return 0;        if ( !b || !c ) return 0;        return 1;    }    LL& ret = dp[n][carry][b][c];    if ( ret != -1 ) return ret;    ret = 0;    LL sum;    int a;    if ( b ) {        if ( c ) {            if ( carry )                add_mod(ret, go(n - num[carry], 0, 1, 1));            else                return 0;        } else {            for (int i = 0; i <= 9; ++ i) {                a = i + carry;                sum = num[i] + num[ a % 10 ];                if ( sum <= n ) {                    add_mod(ret, go(n - sum, a / 10, 1, 0));                    if ( i != 0 )                        add_mod(ret, go(n - sum, a / 10, 1, 1));                }            }        }    } else {        if ( c ) {            for (int i = 0; i <= 9; ++ i) {                a = i + carry;                sum = num[i] + num[ a % 10 ];                if ( sum <= n ) {                    add_mod(ret, go(n - sum, a / 10, 0, 1));                    if ( i != 0 )                        add_mod(ret, go(n - sum, a / 10, 1, 1));                }            }        } else {            for (int i = 0; i <= 9; ++ i)                for (int j = 0; j <= 9; ++ j) {                    a = i + j + carry;                    sum = num[i] + num[j] + num[ a % 10 ];                    if ( sum <= n ) {                        add_mod(ret, go(n - sum, a / 10, 0, 0));                        if ( i != 0 )                            add_mod(ret, go(n - sum, a / 10, 1, 0));                        if ( j != 0 )                            add_mod(ret, go(n - sum, a / 10, 0, 1));                        if ( i != 0 && j != 0 )                            add_mod(ret, go(n - sum, a / 10, 1, 1));                    }                }        }    }    return ret;}

FOJ有奖月赛-2015年10月

G

http://acm.fzu.edu.cn/problem.php?pid=2204
一行上有n个球,可以染成黑或白,要求连续7个不能是同样颜色,求方案数
(开始的颜色,前i个球,最后的段长为j,最后一段的颜色)
然后,枚举最开始放黑或白,进行dp预处理
n>=7
枚举第一段的长度,最后一段的长度,分情况讨论,累加答案。
优化:因为第一个位置放黑白是对称的,所以可以让第一个位置固定为白色,然后得到的答案乘2

int dp[N+5][10][2];void add_mod(int& x, int y) {    x += y; if ( x >= Mod ) x -= Mod;}void init(int n) {    memset(dp, 0, sizeof(dp));    dp[1][1][0] = 1;    for(int i = 1; i < n; ++ i)        for(int j = 1; j <= 6; ++ j)            if ( j <= i )                for (int k = 0; k <= 1; ++ k)                    if ( dp[i][j][k] ) {                        add_mod(dp[i+1][1][!k], dp[i][j][k]);                        add_mod(dp[i+1][j+1][k], dp[i][j][k]);                    }}int main() {    init(N);    int t, cas = 0;    scanf("%d", &t);    while ( t -- ) {        int n;        scanf("%d", &n);        int ans = 0;        if ( n < 7 ) {            ans = (1 << n);        } else {            for (int k = 1; k <= 6; ++ k) {                for (int i = 1; i <= 6; ++ i)                    if ( i <= n - k ) {                        add_mod(ans, dp[n-k][i][0]);                    }                for(int i = 1; k + i <= 6; ++ i)                    if ( i <= n - k ) {                        add_mod(ans, dp[n-k][i][1]);                    }            }            add_mod(ans, ans);        }        printf("Case #%d: %d\n", ++ cas, ans);    }    return 0;}

POJ 2763 Housewife Wind

题意:
给一棵树,边上有权值。两种操作,1)询问两点间路径权值和 2)修改一条边的值
思路:
从《挑战程序设计竞赛》上学到 LCA + 树状数组的解法。
利用 RMQ LCA 中求出的欧拉序列,加上树状数组,可以方便的维护路径和。

void dfs(int fa, int u, int d) { // father, now, depth    vs[timer] = u;       // 记录欧拉序列    id[u] = timer;       // 每个顶点在欧拉序列中的最小下标    dep[timer ++] = d;   // 欧拉序列中每个下标对应深度    // 每条边在欧拉序列中对应一个区间, in[], out[] 分别是左右端点    // 当沿着这条边进入一颗子树的时候,需要加上该边权值    // 当沿着这条边离开子树的时候,要减去该边权值    for(int i = head[u]; i != -1; i = E[i].nxt) {        const Edge& e = E[i];        if ( e.v == fa ) continue;        BIT::upd(timer, w[e.id], n << 1);        in[e.id] = timer;        dfs(u, e.v, d+1);        vs[timer] = u;        BIT::upd(timer, -w[e.id], n << 1);        out[e.id] = timer;        dep[timer ++] = d;    }}

代码

容斥练习

hdu 2204 Eddy’s爱好

题意:求 1 - n 中可以表示成 xyy>1 的数的个数
f(k) : 可以表示成 xk<=n 的数
可以通过容斥求出答案
例如 26=82=43,所以 x6 之前被算了两次,所以 f(6) 的系数是 -1
最后对每个数,都只计了 xk :x 最小,k 最大的形式,所以不会重复。
关于 f(k) 的计算,可以用 f(k) = floor ( pow( n, 1.0 / k + eps ) )
而 acdreamer 给了一种更准确的方法

const int prime[]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59};ll n, ans, tot;ll get(int k) {    ll ret = pow(n, 1.0 / k) + 1e-8;    -- ret;    return ret;}void dfs(int cnt, int pos, int mul) {    if ( cnt ) {        ans += get(mul) * ( ( cnt & 1 ) ? 1 : -1 );    }    if ( cnt == 3 ) return;    for(int i = pos + 1; i < tot; ++ i) {        if ( mul * prime[i] >= 60 ) break;        dfs( cnt + 1, i, mul * prime[i] );    }}int main() {    tot = sizeof(prime) / sizeof(int);    while ( cin >> n ) {        ans = 0;        dfs(0, -1, 1);        cout << ans + 1  << endl;    }    return 0;}

codeforces 588D - Duff in Beach

bi 可以表示成
{0…n-1}{0…n-1}{0…(l%n - 1 + n) % n} 括号中的数字是 ai 中的下标,每个括号中的元素为一段
(i, j) 表示从第0段开始长度为i,以ai中下标为j的元素结束
然后对每个(i, j)算出起始段的可行个数 x,将 x * dp(i, j) 增加到答案。
PS:有爆ll的点。。

0 0
原创粉丝点击