Codecraft-18 and Codeforces Round #458 (Div. 1 + Div. 2, combined)C,D详解

C. Travelling Salesman and Special Numbers
思路:由题意可得,n最多有1000位,那么进行了第一次操作后,它就变成一个不大于1000的数,所以我们可以先预处理1000以内变成1的次数。
第二步,我们再分类讨论,设i是它当前位,i之前有x个1,y到1的操作数为k-1,Size是它的长度
如果第i位是1,那么ans+=第i位为0的所有满足题意的数。也就是Size-i-1的位置中选出y-x个位置放1.也就是C[Size-i-1][y-x]。往后走,x++,i++。
如果第i为为0,则continue。。
这样算其实可能多算了一个,也可能少算了一个。
多算一个什么呢,如果k是1的话,其实它会把1这个数也算进去。
少算什么呢,它本身这个数没有被考虑进去,之前算的都是小于它的数的方案数。
代码如下:

#include<iostream>
#include<string>
#include<vector>
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
ll C[1005][1005];
int dp[1005];
int ones(int num)
{
    int cnt = 0;
    while (num)
    {
        if (num % 2)cnt++;
        num /= 2;
    }
    return cnt;
}
void init()
{
    for (int i = 0;i <= 1000;i++)C[i][0] = 1;
    for (int i = 1;i <= 1000;i++)
        for (int j = 0;j <= i;j++)
            C[i][j] = (C[i - 1][j] + C[i - 1][j - 1])%mod;
    dp[1] = 0;
    for (int i = 2;i <= 1000;i++)
        dp[i] = dp[ones(i)] + 1;
}
vector<int>V;
int main()
{
    ios::sync_with_stdio(0);
    string n;
    int k;
    cin >> n >> k;
    int Size = n.size();
    int used = 0;
    init();
    ll ans = 0;
    if (k == 0)
    {
        puts("1");
        return 0;
    }
    for (int i = 1;i <= 1000;i++)
        if (dp[i] == k - 1)
            V.push_back(i);
    for (int i = 0;i < Size;i++)
    {
        if (n[i] == '0')continue;
        for (auto it : V)
        {
            int nouse = Size - i - 1;
            if (it-used > nouse||used>it)continue;
            ans = (ans + C[nouse][it-used]) % mod;
        }
        used++;
    }
    if (k == 1)
        ans = (ans - 1 + mod) % mod;
    if (dp[used] == k - 1)
        ans = (ans + 1) % mod;
    cout << ans << endl;
}

D. Bash and a Tough Math Puzzle
思路:如果就是简单的修改询问,我们可以用线段树来维护区间gcd,但题目的询问基于区间内有一个数可以被修改的情况下的。这时候我们可以自顶向下来查找我们需要的区间,如果它的两个子区间的gcd%x都不为0,那就不用往下走了,直接NO,如果只有1个就往下走,记录不满足题意的叶子结点个数tmp。如果tmp>1就是NO,否则YES。

#include<iostream>
using namespace std;
const int maxn = 5e5 + 10;
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1
int Gcd[maxn << 2];
int A[maxn], n;
int gcd(int x, int y)
{
    return x == 0 ?y : gcd(y%x, x);
}
void PushUp(int rt)
{
    Gcd[rt] = gcd(Gcd[rt << 1], Gcd[rt << 1 | 1]);
}
void Build(int l, int r, int rt)
{
    if (l == r)
    {
        Gcd[rt] = A[l];
        return;
    }
    int m = (l + r) >> 1;
    Build(ls);
    Build(rs);
    PushUp(rt);
}
void Update(int L, int C, int l, int r, int rt)
{
    if (l == r)
    {
        Gcd[rt] = C;
        return;
    }
    int m = l + r >> 1;
    if (L <= m)Update(L, C, ls);
    else
        Update(L, C, rs);
    PushUp(rt);
}
int Query(int L, int R, int l, int r, int rt)
{
    if (L <= l&&r <= R)
        return Gcd[rt];
    int m = l + r >> 1;
    int ANS = 0;
    if (L <= m)
        ANS = gcd(ANS, Query(L, R, ls));
    if (R > m)
        ANS = gcd(ANS, Query(L, R, rs));
    return ANS;
}
int tmp;
void  check(int L, int R, int x, int l, int r, int rt)
{
    if (l == r)
    {
        if (Gcd[rt] % x != 0)
            tmp++;
        return;
    }
    if (L <= l&&r <= R)
    {
        int m = l + r >> 1;
        if (Gcd[rt << 1] % x != 0 && Gcd[rt << 1 | 1] % x != 0)
            tmp += 2;
        else if ((Gcd[rt << 1] % x != 0))
            check(L, R, x, ls);
        else if (Gcd[rt << 1 | 1] % x != 0)
            check(L, R, x, rs);
        return;

    }
    int m = l + r >> 1;
    if (L <= m)
        check(L, R, x, ls);
    if (R > m)
        check(L, R, x, rs);
}


int main()
{
    //ios::sync_with_stdio(0);

    cin >> n;
    for (int i = 1;i <= n;i++)
        cin >> A[i];
    int Q;
    cin >> Q;
    int opt;
    int l, r, x, y, j;
    Build(1, n, 1);
    for (int i = 1;i <= Q;i++)
    {
        scanf("%d", &opt);
        if (opt == 1)
        {
            scanf("%d %d %d", &l, &r, &x);
            int temp = Query(l, r, 1, n, 1);
            if (temp == x)
                puts("YES");
            else
            {
                tmp = 0;
                check(l, r, x, 1, n, 1);
                if (tmp == 1)
                    puts("YES");
                else
                    puts("NO");
            }
        }
        else
        {
            scanf("%d %d", &j,&y);
            Update(j, y, 1, n, 1);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值