首页 > 其他分享 >Codeforces Round 853 (Div. 2)(C,D)

Codeforces Round 853 (Div. 2)(C,D)

时间:2023-03-03 20:14:27浏览次数:52  
标签:字符 853 int 位置 Codeforces len 序列 Div include

Codeforces Round 853 (Div. 2)(C,D)

C

C

题目大意就是给你\(n\)个数,我们可以按顺序得到接下来的\(m\)个序列,每一个操作是对前面一个序列的第\(p\)个数变成\(v\),保证每次变化后的序列里面的每一个数两两不同,然后我们找到两个序列\(A_i\)和\(A_j\),并且保证\(j>i\),我们可以得到这两个序列里面的数的种类的数量,然后对于每一对序列,求出这个值的和

这个想着还真是麻烦

然后再知乎上看到了大佬的题解

既然直接求出比较麻烦,正难则反

我们可以知道假如每一对都是完全不同时的值,即总值 ,用\(ans\)表达

\[ans=\frac{(m+1)\times m}{2}\times(2\times n) \]

然后我们只要减去那些重复的数

怎么计算那些重复的数,那些数重复出现了多少次呢

对于每一个出现的数,我们都把它存进\(cnt\)里面,然后对于这一个数在多少个序列出现过呢

我们可以记录每一个位置最后一次变化时的序列编号,也是变化后的数第一次出现在的序列的编号,用\(lst\)数组存储,那么如果在下一次如果这一个位置又一次出现变化,那么变化前的数\(a_x\)出现了\(i-lst_x\),\(i\)是此时的序列编号,\(lst_x\)是这个位置的上一次变化的序列编号,那么变化之前的那个数\(a_x\)就出现了\(i-lst_x\)次

然后我们可以得出每一个数出现的在那些不同的序列的次数

对于这些数,只有当两个序列都在这些序列里面,选择的两个序列都出现了同一个数字\(x\),那么\(x\)的贡献就需要减一,则有多少种这样的搭配,就要减少多少个\(1\)

#include <iostream>
#include <unordered_map>
#include <vector>
using namespace std;
const int maxn=2e5+10;
#define int long long 
int t,m,n;
struct node
{
    int p,v;
}b[maxn];
int a[maxn];
unordered_map<int,int>cnt;
int lst[maxn];
void solve()
{
    cin>>n>>m;
    cnt.clear();
    for (int i=1;i<=n;i++)
    {
        cin>>a[i];
        lst[i]=0;
    }
    for (int i=1;i<=m;i++)
    {
        cin>>b[i].p>>b[i].v;
    }
    for (int i=1;i<=m;i++)
    {
        cnt[a[b[i].p]]+=i-lst[b[i].p];
        lst[b[i].p]=i;
        a[b[i].p]=b[i].v;
    }
    int ans=n*m*(m+1);
    for (int i=1;i<=n;i++)
    {
        cnt[a[i]]+=m+1-lst[i];
    }
    for (auto [x,y]:cnt)
    {
        ans=ans-y*(y-1)/2;
    }
    cout<<ans<<'\n';
    return ;
}
signed main ()
{
    cin>>t;
    while (t--)
    {
        solve();
    }
    system ("pause");
    return 0;
}

D

D

这个题大意是给你两个二进制字符串,我们需要通过下面两种操作把第一个字符串变成第二个字符,最后输出操作步骤

有以下两种操作

所谓左移,右边补\(0\),那么对于左移\(k\)再对原来的数进行异或,那么最后面\(k\)个都只是和\(0\)进行异或,没有影响

右移,左边补\(0\),那么最前面的\(k\)个都是和\(0\)异或,那么这些字符也没有影响

我们需要把\(a\)变成\(b\),我先是找到\(b\)的第一个\(1\)的位置\(p\),以\(p\)为界限分别对这两边的不同的位置进行一一变成相同的字符

那么我们可以先把\(p\)左边的字符变成和\(b\)一样的字符

我们从\(p\)往前遍历,把出现不同字符变成应该变成的位置,那么如果我们这个已经匹配好了,后面的操作一定不能影响这个已经匹配好的,然后这也和左移不会影响后面的字符,那么哪一个位置可以实现这样的操作呢?

那就是\(a\)最后一个出现\(1\)的位置,我们需要把这个位置上的\(1\)位移到这个不匹配的位置上去,而且这个位置后面的都是\(0\),所以我们正好改变了这个位置的符号,而且还不会影响这个位置后面的字符

然后对于\(p\)的右边我们也需要进行配对,如果这个没有匹配成功的,我们需要把这个位置的字符变化,并且还不影响左边已经匹配好的字符,我们可以用到右移,我们也是寻找从左边第一个\(1\)的位置匹配(左边是\(00001\)这样的形式,那么左边第一个\(1\)一定是\(p\),不用寻找了)到这个位置上去

#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
using namespace std;
const int maxn=2e5+10;
int a[maxn],b[maxn];
int t,n;
vector<int>ans;
void change(int len)
{
    ans.push_back(len);
    if (len>0)
    {
        for (int i=1;i+len<=n;i++)//保证a[i+len]是未经过变化的,左移右边补0,后面len个不变
        {
            a[i]=a[i]^a[i+len];//i>len-n后面的这一部分不变
        }
    }
    else 
    {
        len=-1*len;
        for (int i=n;i>=len+1;i--)//保证a[i-len]是未经过变化的,右移左边补0,前len个不变
        {
            a[i]=a[i]^a[i-len];//len前面的不变
        }
    }
    return ;
}
void solve()
{
    cin>>n;
    string aa,bb;
    cin>>aa>>bb;
    if (aa==bb)
    {
        cout<<0<<'\n';
        return ;
    }
    aa=" "+aa,bb=" "+bb;
    ans.clear();
    int cnta=0,cntb=0;
    for (int i=1;i<=n;i++)
    {
        int x=aa[i]-'0';
        a[i]=x;
        if (x)
        {
            cnta++;
        }
        int y=bb[i]-'0';
        b[i]=y;
        if (y)
        {
            cntb++;
        }
    }
    if ((cnta&&!cntb)||(cntb&&!cnta))
    {
        cout<<-1<<'\n';
        return ;
    }
    int p=0;
    for (int i=1;i<=n;i++)//左边匹配
    {
        if (b[i]) 
        {
            p=i;
            break;
        }
    }
    for (int i=p;i>=1;i--)//左边匹配
    {
        if (a[i]==b[i]) continue;
        for (int j=n;j>=1;j--)//寻找最右边的1
        {
            if (a[j])
            {
                change(j-i);
                break;
            }
        }
    }
    for (int i=p+1;i<=n;i++)//右边边匹配
    {
        if (a[i]!=b[i])
        {
            change(p-i);//p是最左边的1
        }
    }
    int cnt=ans.size();
    if (!cnt)
    {
        cout<<0<<'\n';
        return ;
    }
    cout<<cnt<<'\n';
    for (auto x:ans)
    {
        cout<<x<<" ";
    }
    cout<<'\n';
    return ;
}
signed main ()
{
    cin>>t;
    while (t--)
    {
        solve();
    }
    system ("pause");
    return 0;
}

标签:字符,853,int,位置,Codeforces,len,序列,Div,include
From: https://www.cnblogs.com/righting/p/17176812.html

相关文章