Codeforces Round #435 (Div. 2)C,D,E,F题目详解

C题题意:用n个不同的数异或变成x。
思路:博主一开始的想法是一个偶数和它相邻的奇数异或值为1,那么我只要构造出许多个1,然后判断有奇数个1还是偶数个1就行了。如果是偶数个1,那么相当于前面异或值为0了,那么在加一个x就行了。同理奇数个1相当于前面异或值为1,那么最后一个数为x-1或者x+1(需判断一下哪个可行)。
依然觉得想法可行,但有许多细节要注意。下面介绍一个思路更加清晰的想法。
思路2:
n=1时,直接输出x。
n=2时,如果x=0,则输出no,否则输出0和x
n=3时输出 (1<<17)|x,(1<<17|1<<18),1<<18
n>3时temp=1^2^3^…^(n-3),x=x^temp,前面的数为1,2,3…(n-3),然后再做一遍n=3即可。
代码如下:

#include<iostream>
using namespace std;
int main()
{
    int n, x;
    cin >> n >> x;
    if (n == 1)
    {
        puts("YES");
        cout << x << endl;
    }
    else if (n == 2)
    {
        if (x == 0)puts("NO");
        else printf("YES\n0 %d\n", x);
    }
    else if (n == 3)
    {
        long long tmp1 = x | (1 << 17);
        long long tmp2 = (1 << 17) | (1 << 18);
        long long tmp3 = 1 << 18;
        puts("YES");
        printf("%lld %lld %lld\n", tmp1, tmp2, tmp3);
    }
    else
    {
        long long tmp = 0;
        puts("YES");
        for (int i = 1;i <= n - 3;i++)
        {
            printf("%d ", i);
            tmp ^= i;
        }
        x ^= tmp;
        long long tmp1 = x | (1 << 17);
        long long tmp2 = (1 << 17) | (1 << 18);
        long long tmp3 = 1 << 18;
        printf("%lld %lld %lld\n", tmp1, tmp2, tmp3);

    }
    return 0;
}

D题题意:考2分的好题啊。。一道交互题,有一串二进制串,你可以通过询问的两个相同长度的二进制串的汉明距离(即两个串中有几处不同),分析出其中任意一个1和0在哪个位置,并输出该位置。

思路:我们只要找到一个1和0就行了,如果一个串全是1或全是0,那么下次询问肯定就不包括该串了,我们可以通过二分不断缩小询问的长度,来找到一个1和0 的位置。
代码如下:

#include<iostream>
using namespace std;
int s;
int n;
int query(int l, int r)
{
    int i;
    printf("? ");
    for (i = 1;i < l;i++)printf("0");
    for (;i <= r;i++)printf("1");
    for (;i <= n;i++)printf("0");
    puts("");
    fflush(stdout);
    int x;
    scanf("%d", &x);
    return x;
}
int p0, p1;
void solve(int l, int r)
{

    if (p0&&p1)return;
    int mid = l + r >> 1;
    int x = query(l, mid);
    if (s + mid - l + 1 == x)p0 = l, solve(mid + 1, r);
    else if (s - (mid - l + 1) == x)p1 = l, solve(mid + 1, r);
    else solve(l, mid);
}
int main()
{

    cin >> n;
    s = query(0, 0);
    solve(1, n);
    printf("! %d %d", p0, p1);
    return 0;
}

E题题意:简单来说,其实就是在b中找一段连续的长度和a相等的序列,序列值为 b[i]-b[i+1]+b[i+2]-b[i+3]…该序列值和a的序列值最接近即可。另外还可修改a的某区间的值。
题意说的有点模糊,大家可以看看原题题意。。
思路:仔细想想其实这道题很。。简单。。
首先b的序列值可以预处理一下,博主用的树状数组存b的值,但偶数位存的是它的相反数,那么我们再求一个区间的序列值就只需判断第一位是偶数位还是奇数位,然后可决定树状数组求和的值取反或者不取。
再说a,如果对a修改的区间长度为偶数,那么其实相当于没修改,如果奇数的话,再判断,最前面那位是正还是负,就可知道对序列值加还是减了。
思路很简单,仔细思考就出来了。。
代码如下:

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
typedef long long ll;
#define lowbit(x) ((x)&(-x))
const int maxn = 100005;
ll sum[maxn];
void insert(ll num, int idx)
{
    while (idx < maxn)
    {
        sum[idx] += num;
        idx += lowbit(idx);
    }
}
ll query(int idx)
{
    ll Sum = 0;
    while (idx)
    {
        Sum += sum[idx];
        idx -= lowbit(idx);
    }
    return Sum;
}
vector<ll>V;
int main()
{
    int n, m, q;
    cin >> n >> m >> q;
    ll sum = 0;
    ll temp;
    for (int i = 1;i <= n;i++)
    {
        scanf("%lld", &temp);
        if (i % 2)sum += temp;
        else sum -= temp;
    }

    for (int i = 1;i <= m;i++)
    {
        scanf("%lld", &temp);
        if (i % 2)insert(temp, i);
        else insert(-temp, i);
    }
    for (int i = 1;i <= m-n+1;i++)
    {
        int l = i, r = i + n - 1;
        ll tmp1 = query(r) - query(i - 1);
        if (i % 2)
            V.push_back(tmp1);
        else V.push_back(-tmp1);
    }
    sort(V.begin(), V.end());
    int l, r;
    ll x;

    int first = lower_bound(V.begin(), V.end(), sum) - V.begin();
    if (first == V.size())first--;
    int sec = first;
    if (first != 0)
        sec = first - 1;
    printf("%lld\n", min(abs(V[first] - sum), abs(V[sec] - sum)));



    for (int i = 1;i <= q;i++)
    {
        scanf("%d%d%lld", &l, &r, &x);
        if ((r - l) % 2)
        {
            int idx = lower_bound(V.begin(), V.end(), sum) - V.begin();
            if (idx == V.size())idx--;
            int bef = idx;
            if (idx != 0)
                 bef = idx - 1;
            printf("%lld\n", min(abs(V[idx] - sum), abs(V[bef] - sum)));

        }
        else
        {
            if (l % 2)sum += x;
            else sum -= x;
            int idx = lower_bound(V.begin(), V.end(), sum)-V.begin();
            if (idx == V.size())idx--;
            int bef = idx;
            if (idx != 0)
                bef = idx - 1;
            printf("%lld\n", min(abs(V[idx] - sum), abs(V[bef] - sum)));
        }
    }
    return 0;
}

F题题意:给你n个字符串,有q个询问。
1 a b则在所有的[l,r] (a<=l<=r<=b)中找到(r-l+1)*LCP(sl,sl+1,sl+2….,sr-1,sr)的最大值。LCP意为最长公共前缀。
2 x y将x位置的字符串替换成y。
好题啊,和以前做的简单线段树完全不一样。
博主一开始的思路就是线段树维护该区间的最值,那么区间合并的时候该怎么合并呢,合并后有些区间是落在两个区间中,所以求最值也得把这些考虑进去。
首先再求一段value时,其实就是这一段中的最小的lcp*长度。
所以我们开两个vector,L,R.R中存一个序列以某个数为最小值的左边界在哪里,同理L中存一个序列以某个数为最小值的右边界在哪里。那么合并后的最值就可以通过这个计算了。
可能有点难理解。。读者可以结合代码想一想。
代码如下:

#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn = 100005;
string s[maxn];
int lca[maxn];

struct node 
{
    int ans;
    vector<pair<int, int> >L, R;
}nodes[maxn << 2];

node merge(const node &L,const node &R, int m)
{
    node res;
    res.ans = max(L.ans, R.ans);
    for (auto i =L.R.end() - 1, j = R.L.end() - 1;i >= L.R.begin();i--)
    {
        while (j>R.L.begin()&&j->first < i->first)
            j--;
        if (j->first < i->first)break;
        res.ans = max(res.ans, (j->second - i->second + 1)*min(lca[m], i->first));
        if (i == L.R.begin())break;
    }

    for (auto i = R.L.end() - 1, j = L.R.end() - 1;i >= R.L.begin();i--)
    {
        while (j > L.R.begin() && j->first < i->first)
            j--;
        if (j->first < i->first)break;
        res.ans = max(res.ans, (i->second - j->second + 1)*min(lca[m], i->first));
        if (i == R.L.begin())break;
    }

    res.L = L.L;
    for (auto i : R.L)
    {
        i.first = min(lca[m], i.first);
        if (i.first >= res.L.back().first)
            res.L.back().second = i.second;
        else
            res.L.push_back(i);
    }

    res.R = R.R;
    for (auto i : L.R)
    {
        i.first = min(lca[m], i.first);
        if (i.first >= res.R.back().first)
            res.R.back().second = i.second;
        else
            res.R.push_back(i);
    }
    return res;
}


void build(int p, int l, int r)
{
    if (l == r)
    {
        nodes[p].ans = s[l].size();
        nodes[p].L = nodes[p].R = { {s[l].size(),l} };
        return;
    }
    int mid = l + r >> 1;
    build(p << 1, l, mid);
    build(p << 1 | 1, mid + 1, r);
    nodes[p]=merge(nodes[p << 1],nodes[ p << 1 | 1], mid);
}

void update(int p, int l, int r,int c)
{
    if (l == r)
    {
        nodes[p].ans = s[c].size();
        nodes[p].L = nodes[p].R = { {s[c].size(),c} };
        return;
    }
    int mid = l + r >> 1;
    if (c <= mid)
        update(p << 1, l, mid, c);
    else update(p << 1 | 1, mid + 1, r, c);
    nodes[p]=merge(nodes[p << 1],nodes[ p << 1 | 1], mid);
}

node G(int p, int L, int R, int l, int r)
{
    if (L <= l&&r <= R)
        return nodes[p];
    int mid = l + r>>1;
    if (R <= mid)
        return G(p << 1, L, R, l, mid);
    else if (L > mid)
        return G(p << 1 | 1, L, R, mid + 1, r);
    else return merge(G(p << 1, L, mid, l, mid), G(p << 1 | 1, mid + 1, R, mid + 1, r),mid);
}

int main()
{
    int n, q;
    cin >> n >> q;
    for (int i = 1;i <= n;i++)
        cin >> s[i];
    for (int i = 1;i < n;i++)
        while (lca[i]<s[i].size()&&s[i][lca[i]] == s[i + 1][lca[i]])
            lca[i]++;
    build(1, 1, n);
    int c;
    for (int i = 1;i <= q;i++)
    {
        scanf("%d", &c);
        if (c == 1)
        {
            int a, b;
            scanf("%d%d", &a, &b);
            printf("%d\n", G(1,a, b, 1, n).ans);
        }
        else
        {
            int x;
            string y;
            scanf("%d", &x);
            cin >> y;
            s[x] = y;
            if (x > 1)
            {
                lca[x - 1] = 0;
                while (lca[x-1]<s[x-1].size()&&s[x - 1][lca[x - 1]] == s[x][lca[x - 1]])
                    lca[x - 1]++;
                update(1, 1, n, x - 1);
            }
            if (x < n)
            {
                lca[x] = 0;
                while (lca[x]<s[x].size()&&s[x][lca[x]] == s[x + 1][lca[x]])
                    lca[x]++;
                update(1, 1, n, x);
            }
            update(1, 1, n, x);
        }
    }
}

好题啊,特别是合并那里对L和R的处理。妙不可言。。希望读者一定要搞懂!另外,博主一不小心把所有的lcp数组全部打成了lca,大家凑合着看看。。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值