【log】近期刷题 - 2015.9 - 2015.10

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的点。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值