1. 程式人生 > >Codeforces Round #435 (Div. 2)C,D,E,F題目詳解

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,大家湊合著看看。。