【MZ】CF 356A - 356D #207 (Div. 1)

A. Knight Tournament

problem

输入 n m 和 m 行 每行 l r x 表示把区间[l, r]中除了x还没有值的变成x

问最后每个n的值分别是多少,没有就是0

think

线段树

code

int val[333333<<2];

void update(int l, int r, int k, int L, int R, int x){
    if(L > R || val[k] > 0) return;
    if(l==L && r==R){
        val[k] = x;
        return;
    }
    int mid = (l+r) >> 1;
    if(R <= mid) update(l, mid, k<<1, L, R, x);
    else if(L > mid) update(mid+1, r, k<<1|1, L, R, x);
    else{
        update(l, mid, k<<1, L, mid, x);
        update(mid+1, r, k<<1|1, mid+1, R, x);
    }
}

int query(int l, int r, int k, int p, int ans){
    if(l==r){
        return val[k] == 0 ? ans : val[k];
    }
    if(val[k] > 0) ans = val[k];
    int mid = (l+r) >> 1;
    if(p <= mid) return query(l, mid, k<<1, p, ans);
    else return query(mid+1, r, k<<1|1, p, ans);
}

int main() {
    int n, m;
    scanf("%d%d", &n, &m);
    int l, r, x;
    for(int i = 0; i < m; ++i){
        scanf("%d%d%d", &l, &r, &x);
        update(1, n, 1, l, x-1, x);
        update(1, n, 1, x+1, r, x);
    }
    for(int i = 1; i <= n; ++i){
        printf("%d ", query(1, n, 1, i, 0));
    }
    return 0;
}

B. Xenia and Hamming

problem

输入n m 和两个串x y,n*|x| 和 m*|y|一定相等n m 1e12; |x|  |y| 1e6

问n个x组成的串 和 m个y组成的串 对应位置不一样的有多少个

think

看成一样的有多少个了。于是就求的一样的,然后减。

求最小公倍数长度的串的一样数,再乘以倍数。

看最小公倍数,画一画就很明显。记录每个字母在每个最大公约数的余数位置的个数。

code

const int N = 1111111;
char x[N], y[N];
LL dp[N][26];

LL gcd(LL a, LL b){
    return (a==0 ? b : gcd(b%a, a));
}

int main(){
    LL n, m;
    scanf("%I64d%I64d%s%s", &n, &m, x, y);
    LL xx = (LL)strlen(x);
    LL yy = (LL)strlen(y);
    LL g = gcd(xx, yy);
    for(int i = 0; i < yy; ++i){
        ++dp[i%g][y[i]-'a'];
    }
    LL ans = 0;
    for(int i = 0; i < xx; ++i){
        ans += dp[i%g][x[i]-'a' ];
    }
    ans *= n*g/yy;
    printf("%I64d\n", n*xx-ans);
    return 0;
}


C. Compartments

problem

n个车厢,每个车厢最多4个人,给你每个车厢有多少我们的人,问最少我们有几个人和别人换票,才能使得每个车厢有0或3或4个我们的人。

think

构造。

code

int a[5];

int solve(int a0, int a1, int a2, int a3, int a4, int n3, int n4){
    if(a3 + a4 >= n3 + n4) return a1 + a2 * 2;
    else if(a2 + a3 + a4 >= n3 + n4) return a1 + (a2 + a3 + a4 - n3 - n4) * 2 + ((a4 - n4) > 0 ? (a4 - n4) : 0);
    else return (a1 + a2 + a3 + a4 - n3 - n4) + ((a4 - n4) > 0 ? (a4 - n4) : 0);
}

int main() {
    int n, sum = 0, x;
    scanf("%d", &n);
    for(int i = 0; i < n; ++i){
        scanf("%d", &x);
        ++a[x];
        sum += x;
    }
    if(sum==1 || sum==2 || sum==5){
        puts("-1");
        return 0;
    }
    int ans = sum;
    for(int i = 0, k = 1; 4 * i <= sum; i += k){
        if((sum - 4 * i) % 3){
            continue;
        }
        else k = 3;
        int j = (sum - 4 * i) / 3;
        if(i + j > n) continue;
        int tmp = solve(a[0], a[1], a[2], a[3], a[4], j, i);
        ans = min(ans, tmp);
    }
    cout<<ans<<endl;
    return 0;
}

D. Bags and Coins

problem

n个袋子m个硬币,给你每个袋子里面的硬币数,求一种方法满足条件并且每个硬币都不在外面

输出n行,每一行输出直接里面有几个硬币,直接里面有几个袋子,这几个袋子分别是第几个袋子

think

关键就是找到包含最大的那个袋子的和正好是m的方案

除了最大那个袋子,与他和是m的那个方案其他的都标记为single表示他不在别的袋子里面,别的袋子也不在他里面。

这个我用了背包,会超市, 特判最大是1的情况。

code

int dp[N], pre[N], single[N];
int aa[N];
struct point{
    int a, id;
}p[N];
int bef[N];
int n, m, mx;
LL sum;

bool cmp(point x, point y){
    return x.a < y.a;
}

void out(){
    for(int i = m; i > 0; ){
        int j = pre[i];
        i -= p[j].a;
        if(j == 0) break;
        single[p[j].id] = 1;
    }
    for(int i = 1; i <= n; ++i){
        if(single[i]){
            printf("%d %d\n", aa[i], 0);
        }
        else {
            int tmp1 = 0, tmp2, ii = bef[i];
            while(ii && single[ii]) ii = bef[ii];
            if(ii > 0) printf("%d %d %d\n",aa[i] - aa[ii], 1, ii);
            else printf("%d %d\n",aa[i], 0);
        }
    }
}

void solve(){
    if(p[n].a == 1){
        for(int i = 1; i <= m; ++i) printf("%d %d\n", 1, 0);
        for(int i = m + 1; i <= n; ++i) printf("%d %d %d\n", 0, 1, i-1);
        return;
    }
    dp[p[n].a] = 1;
    if(p[n].a == m) {
        out();
        return;
    }
    int last = p[n].a;
    for(int i = n-1; i >= 1; --i){
        for(int j = m - p[i].a; j >= last; --j){
            if(dp[j]==0 || dp[j+p[i].a]==1) continue;
            pre[j+p[i].a] = i;
            dp[j+p[i].a] = 1;
            if(j+p[i].a==m){
                out();
                return;
            }
        }
    }
    puts("-1");
}

int main (){
    scanf("%d%d", &n, &m);
    sum = mx = 0;
    for(int i = 1; i <= n; ++i){
        scanf("%d", &p[i].a);
        aa[i] = p[i].a;
        p[i].id = i;
        mx = max(mx, p[i].a);
        sum += (LL)aa[i];
    }
    if(mx > m || sum < m){
        puts("-1");
        return 0;
    }
    sort(p + 1, p + n + 1, cmp);
    bef[p[1].id] = 0;
    for(int i = 2; i <= n; ++i) bef[p[i].id] = p[i-1].id;
    solve();
    return 0;
}






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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值