CSA Round #40 (Div. 2 only)

Erase Value

题意:

给出N个数,每个数都在[1, 1000]范围内,选择一个数,删除这N个数中出现的所有该数,使得所剩数的和最小

分析:

暴力?

代码:

int d[1010];
int main()
{
    int n, x, mx = 0, s = 0;

    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &x);
        s += x;
        d[x] += x;
        mx = max(mx, d[x]);
    } 
    printf("%d\n", s - mx);
}

Move the Bishop

题意:

在8*8棋盘中,移动仅限对角线移动,求某个点到另一个点的最小移动次数

分析:

BFS?
题解是按照棋盘的染色来判定,只有同色才可达,否则不可达。可达情况下,相同点答案为0,若在同一对角线上则答案为1,否则为2(可以证明任意两个同色点所在四条对角线的上侧交点和下侧交点必然至少有一个在棋盘范围内)

代码:

// BFS
#define maxn 10
int d[maxn][maxn], v[maxn][maxn];
int dr[4][2] = {-1, 1, 1, -1, 1, 1, -1, -1};
queue<int> qx;
queue<int> qy;
void work(int x, int y) {
    qx.push(x);
    qy.push(y);
    v[x][y] = 1;
    while (!qx.empty()) {
        int sx = qx.front(), sy = qy.front();
        qx.pop();qy.pop();
        for (int i = 1; i <= 8; i++) {
            for (int j = 0; j < 4; j++) {
                int ex = sx + dr[j][0] * i, 
                    ey = sy + dr[j][1] * i;
                if (ex > 0 && ex <= 8 && ey > 0 && ey <= 8 && !v[ex][ey]) {
                    d[ex][ey] = d[sx][sy] + 1;
                    qx.push(ex);
                    qy.push(ey);
                    v[ex][ey] = 1;
                }
            }
        }
    }
} 
int main()
{
    int x, y, z, p;

    scanf("%d%d%d%d", &x, &y, &z, &p);
    memset(d, -1, sizeof d);
    d[x][y] = 0;
    work(x, y);
    printf("%d\n",d[z][p]);
}
// Color
int main()
{
    int a, b, c, d;

    scanf("%d%d%d%d", &a, &b, &c, &d);
    if ((a + b) % 2 != (c + d) % 2) {
        puts("-1");
    } else {
        if (a == c && b == d) {
            puts("0");
        } else if (b - a == d - c || a + b == c + d) {
            puts("1");
        } else {
            puts("2");
        }
    }
}

Switch the Lights

题意:

有N个灯,初始状态给定,有N个开关,第i个开关的控制范围为 [i,Ri] ,花费为 Ci , 求出使得所有灯关闭的最小费用,若无法关闭所有灯则输出-1

分析:

处理到第i个灯时其前i-1个灯必然要全部关闭,而第i次操作要使第i个灯关闭,如果该灯现在就是关闭的则不使用该开关,否则使用该开关。所以只有一个固定的结果,不存在最优值,也不会出现无解的情况。
直接思路就是线段树区间add单点查询,记录该灯被操作的次数,复杂度 O(nlogn)
另一种方法记录边界,state变量记录操作了几次,操作开关或者遇到操作边界则修改state。

代码:

// segment tree + lazy   O(n * logn)
#define maxn 100010
struct tree {
    int le, rig;
    int sum, cov;
} t[maxn << 2];
void build(int id, int l, int r) {
    t[id].le = l;
    t[id].rig = r;
    t[id].cov = 0;
    if (l == r) {
        t[id].sum = 0;
        return;
    }
    int mid = (l + r) >> 1;
    build(id * 2, l, mid);
    build(id * 2 + 1, mid + 1, r);
    t[id].sum = t[id * 2].sum + t[id * 2 + 1].sum;
}
void update(int id, int l, int r, int val) {
    if (t[id].le == l && t[id].rig == r) {
        t[id].cov += val;
        return;
    }
    int mid = (t[id].le + t[id].rig) >> 1;
    t[id].sum += t[id].cov * (t[id].rig - t[id].le + 1) + val * (r - l + 1);
    t[id * 2].cov += t[id].cov;
    t[id * 2 + 1].cov += t[id].cov;
    t[id].cov = 0;
    if (r <= mid) {
        update(id * 2, l, r, val);
    } else if (l > mid) {
        update(id * 2 + 1, l, r, val);
    } else {
        update(id * 2, l, mid, val);
        update(id * 2 + 1, mid + 1, r, val);
    }
}
int query(int id, int l, int r) {
    if (t[id].le == l && t[id].rig == r) {
        return t[id].sum + t[id].cov * (t[id].le + t[id].rig - 1);
    }
    int mid = (t[id].le + t[id].rig) >> 1;
    t[id].sum += t[id].cov * (t[id].rig - t[id].le + 1);
    t[id * 2].cov += t[id].cov;
    t[id * 2 + 1].cov += t[id].cov;
    t[id].cov = 0;
    if (r <= mid) {
        return query(id * 2, l, r);
    } else if (l > mid) {
        return query(id * 2 + 1, l, r);
    } else {
        return query(id * 2, l, mid) + query(id * 2 + 1, mid + 1, r);
    }
}
char s[maxn];
int b[maxn];
int main()
{
    int n, x;
    ll ans = 0;

    scanf("%d", &n);
    build(1, 1, n);
    scanf("%s", s);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &b[i]);
    }
    for (int i = 1; i <= n; i++) {
        scanf("%d", &x);
        int y = query(1, i, i);
        if ((y & 1) && s[i - 1] == '0' || !(y & 1) && s[i - 1] == '1') {
            ans += x;
            update(1, i, b[i], 1);
        }
    }
    printf("%lld\n", ans);
}
// O(n)
#define maxn 100010
int a[maxn], b[maxn];
char s[maxn];
int main() 
{
    int n, x, state = 0;
    ll ans = 0;

    scanf("%d%s", &n, s);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
    }   
    for (int i = 1; i <= n; i++) {
        scanf("%d", &x);
        if (state ^ (s[i - 1] - '0')) {
            state ^= 1;
            b[a[i]] ^= 1;
            ans += x;
        }
        state ^= b[i];
    }
    printf("%lld\n", ans);
}

Restricted Permutations

题意:

N个数,每个数与其后一个数的相对位置给定(其前或其后),求满足条件的排列的数目

分析:

DP,用 d[i][j] 记录处理完第i位置,其前面有j个数的排列的数目
状态转移:如果a[i]为1,则其应在i-1的后面,对i-1中的每个j,i可以扩展出来的状态为 j+1..i1 。 如果a[i]为0,则其应在i-1的前面,对i-1中的每个j,i可以扩展出来的状态为 0..j

代码:

#define mod 1000000007
#define maxn 2010
int n, a[maxn];
ll ans, d[maxn];
int main()
{
    scanf("%d", &n);
    memset(d, 0, sizeof d);
    d[0] = 1;
    for (int i = 1; i < n; i++) {
        scanf("%d", &a[i]);
        if (a[i] == 1) {
            ll x = d[0];
            for (int j = 1; j <= i; j++) {
                ll y = (x + d[j]) % mod;
                d[j] = x % mod;
                x = y;
            }
            d[0] = 0;
        } else {
            ll x = 0;
            for (int j = i; j >= 0; j--) {
                x = (x + d[j]) % mod;
                d[j] = x % mod;
            }
        }
    } 
    for (int i = 0; i <= 2000; i++) {
        ans = (ans + d[i]) % mod;
    }
    printf("%lld\n", ans);
}

Direct the Graph

待补

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值