7.30个人训练总结

Codeforces 1554C 题目链接:点击这里传送

在这里插入图片描述

题意:
给定n和m,要求构造一个序列为 n ⨁ \bigoplus 0, n ⨁ \bigoplus 1, n ⨁ \bigoplus 2 … n ⨁ \bigoplus m。求未在这个序列中出现的最小自然数。
思路:
关键公式: n ⨁ \bigoplus k =m ⇐ \Leftarrow ⇒ \Rightarrow n ⨁ \bigoplus m=k
∵ \because n ⨁ \bigoplus i=k ,i ∈ \in [0,m]
∴ \therefore n ⨁ \bigoplus k=i ,i ∈ \in [0,m]
这是题目中合法的情况,要使k这个数字不存在于题目构造的序列的话,则要满足
n ⨁ \bigoplus k<0 (舍去) 或者 n ⨁ \bigoplus k >m
即为 n ⨁ \bigoplus k ≥ \geq m+1 ,这就是题目简化后要求求得的式子
我们需要构造一个数k,k和n的异或值要 ≥ \geq m+1

  1. n ≥ \geq m+1,k=0 (n ⨁ \bigoplus k=n)

  2. n<m+1 化成二进制,令p=m+1,n补好前导0,一位一位比较过去,分成以下四种情况

    • n[i]=0 p[i]=1 k[i]=1
    • n[i]=1 p[i]=1 k[i]=0
    • n[i]=0 p[i]=0 k[i]=0
    • 重点 n[i]=1 p[i]=0 k[i]=0 后n[i] ⨁ \bigoplus k[i]=1,此时构造出来的数已经确保已经大于p了,标记一下,之后的k[i]全部设为0(还好样例有这种情况,不然不知道要调多久)
#include<bits/stdc++.h>
using namespace std;
int t,n,m,k,p;
string a,b;
string ans;
string get_b(int n)
{
    string ans="";
    while(n)
    {
        char temp='0'+n%2;
        ans=temp+ans;
        n/=2;
    }
    return ans;
}
int get_o(string s)
{
    int ans=0;
    for(int i=0;i<s.length();i++)
    {
        ans=ans*2+(s[i]-'0');
    }
    return ans;
}
void add_pre_0()
{
    int d=b.length()-a.length();
        string pre_0="";
        for(int i=1;i<=d;i++)
        {
            pre_0+='0';
        }
        a=pre_0+a;//添加前导0
}
void solve()
{
    ans="";
        int flag=0;
        for(int i=0;i<a.length();i++)
        {
            if(flag==1) ans+='0';
            else
            {
                 if(a[i]=='0'&&b[i]=='1') ans+='1';
                 else if(a[i]=='1'&&b[i]=='1') ans+='0';
                 else if(a[i]=='0'&&b[i]=='0')
                 {
                     ans+='0';

                 }
                 else if(a[i]=='1'&&b[i]=='0')
                 {
                     ans+='0';
                     flag=1;
                 }
            }
        }
}
int main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    cin>>t;
    while(t--)
    {
        cin>>n>>m;
        p=m+1;
        if(n>=m+1)
        {
            cout<<0<<endl;
            continue;
        }
        //n<m
        a=get_b(n);
        b=get_b(p);
        add_pre_0();
        solve();
        cout<<get_o(ans)<<endl;
    }


    return 0;
}

Codeforces 1554D 题目链接:点击这里传送

在这里插入图片描述

题意:
要求你构造一个长度为n的字符串,这个字符串中每个子串出现的个数都为奇数
思路:
奇数+偶数=奇数!!! 奇数+偶数=奇数!!! 奇数+偶数=奇数!!!
假设n为偶数的情况
一个字符串为n个’a’,那么a出现了偶数次,aa出现了奇数次,aaa出现了偶数次…
一字符串为n-1个’a’,那么a出现了奇数次,aa出现了偶数次,aaa出现了奇数次…
∵ \because 奇数+偶数=奇数
∴ \therefore 我们只需合并上述两个字符串,他们的子串就一定产生了奇数次
构造方法:
n为偶数
a + a + . . . + a + a ⏟ n / 2 − 1 \underbrace{a+a+...+a+a}_{n/2-1} n/21 a+a+...+a+a +b+ a + a + . . . + a + a ⏟ n / 2 \underbrace{a+a+...+a+a}_{n/2} n/2 a+a+...+a+a
n为奇数
a + a + . . . + a + a ⏟ n / 2 − 1 \underbrace{a+a+...+a+a}_{n/2-1} n/21 a+a+...+a+a +bc+ a + a + . . . + a + a ⏟ n / 2 \underbrace{a+a+...+a+a}_{n/2} n/2 a+a+...+a+a

#include<bits/stdc++.h>
using namespace std;
int t,n;
string s;
int main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    cin>>t;
    while(t--)
    {
        cin>>n;
        s="";
        if(n%2==1)
        {
            for(int i=1;i<=n;i++)
            {
                if(i==n/2)
                {
                    s+="bc";
                    i++;
                }
                else s+='a';
            }
        }
        else
        {
            for(int i=1;i<=n;i++)
            {
                if(i==n/2) s+='b';
                else s+='a';
            }
        }
        cout<<s<<endl;
    }

    return 0;
}

Codeforces 1203C 题目链接:点击这里传送

在这里插入图片描述
题意:
给出一堆数字,让你求出他们的公共约数的个数
思路:
一轮gcd下来求出所有数的最大公约数。这样所有的公共约数就只会出现在[1,最大公约数]的范围内。题目就转化成了求这个最大公约数的约数的个数。从[1, g c d \sqrt{gcd} gcd ]这个范围找,如果找到一个能被整除的就使答案加2(i和gcd ÷ \div ÷i,gcd ÷ \div ÷i没有枚举到).最后特判一下 g c d \sqrt{gcd} gcd 是否为整数,如果是整数的话会重复,让答案减1。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define MAXN 400005
ll a[MAXN];
map <ll,int> mp;
int n;
ll gcd(ll a,ll b)
{
    if(a<b) swap(a,b);
    while(a%b)
    {
        ll temp=a%b;
        a=b;
        b=temp;
    }
    return b;
}
void pre()
{
    for(ll i=1;i*i<=1000000000000;i++) mp[i*i]++;
}
int main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    pre();
    ll ans=0;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        if(i==1) ans=a[i];
        else ans=gcd(ans,a[i]);
    }
    if(ans==1)
    {
        cout<<1<<endl;
        return 0;
    }
    int cnt=0;

    for(ll i=1;i<=sqrt(ans);i++)
    {
        if(ans%i==0) cnt+=2;
    }
    if(mp[ans]!=0)
    cnt--;
    cout<<cnt;
    return 0;
}

Codeforces 1203B 题目链接:点击这里传送

在这里插入图片描述
题意:
给出4 × \times ×n条边,问你是否能组成n个面积相同的矩形
思路:

  • 矩形:对边相同,也就是说边数肯定是成双成对出现的,不会有孤儿
  • 面积:贪心思路,排序后一个指针定在最小边上,一个指针定在最大边上,每次操作的乘积都相同

满足上述两个条件即可实现题意

#include<bits/stdc++.h>
using namespace std;
int t,n;
#define MAXN 905
int a[MAXN];

int main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    cin>>t;
    while(t--)
    {
        memset(a,0,sizeof(a));
        cin>>n;n*=4;
        for(int i=1;i<=n;i++) cin>>a[i];
        sort(a+1,a+1+n);
        int ans=a[1]*a[n];
        int f1=1;int f2=1;
        for(int i=1, j=n;i<j;i+=2,j-=2)
        {
            if(a[i]!=a[i+1]||a[j]!=a[j-1]) f1=0;
            if(a[i]*a[j]!=ans) f2=0;
            if(f1==0||f2==0) break;
        }
        if(f1==0||f2==0)
        {
            cout<<"No"<<endl;
        }
        else cout<<"Yes"<<endl;
    }
    return 0;
}

Codeforces 1203D1 题目链接:点击这里传送

在这里插入图片描述
题意:
给出一个字符串和一个他的子串。要求你在第一个字符串中删除某个连续区间所有的字符串仍能在剩余的字符串中找到这个给定的子串。求这个区间长度的最大值(字符串长度<200)
思路:
n最多只有200的话就很简单了,直接双重枚举l和r,判断剩余字符串的合法性

#include<bits/stdc++.h>
using namespace std;
string a,b;
int ans=0;
//b是a的子串
#define MAXN 205
bool ok(int l,int r)
{
    int cnt=0;
    for(int i=0;i<a.length();i++)
    {
        if(i>=l&&i<=r) continue;
        if(a[i]==b[cnt]&&cnt<b.length()) cnt++;
    }
    if(cnt==b.length()) return true;
    return false;
}
int main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    cin>>a>>b;
    for(int l=0;l<a.length();l++)
    {
        for(int r=l;r<a.length();r++)
        {
            if(ok(l,r)) ans=max(ans,r-l+1);
        }
    }


    cout<<ans;

    return 0;
}

Codeforces 1203D2 题目链接:点击这里传送

在这里插入图片描述
题意:
字符串最大长度从20变成了2e5,其他没有任何区别
思路:
删掉字符串中的一段区间依然能找到给定子串,一共分三种情况

  • 删的是子串开头前的所有区间
  • 删的是子串某两个相邻元素的位置的中间的元素
    假设A为abcdeabca,那么删掉的就是a, b , c , d , e ⏟ 4 \underbrace{b,c,d,e}_{4} 4 b,c,d,e,a,或a, b , c ⏟ 2 \underbrace{b,c}_{2} 2 b,c,a或a, b , c , d , e , a , b , c , a ⏟ 8 \underbrace{b,c,d,e,a,b,c,a}_{8} 8 b,c,d,e,a,b,c,a,a
    仔细观察这个例子,我们可以定义两个数组。一个是b串某一元素第一次出现的合法的位置,一个是b串某一元素最后一次出现的合法的位置。(可能有点绕)。画个图理解理解,假设i是b串某个元素出现的位置
    ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ b [ i ] ⏟ 包 括 b [ i ] 之 前 的 元 素 起 码 出 现 了 一 次 \underbrace{************b[i]}_{包括b[i]之前的元素起码出现了一次} b[i] b[i]+************************+ b [ i + 1 ] ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ⏟ 包 括 b [ i + 1 ] 之 后 的 元 素 起 码 出 现 了 一 次 \underbrace{b[i+1]********************}_{包括b[i+1]之后的元素起码出现了一次} b[i+1] b[i+1]
    这种情况下删除的区间大小即为 last[i+1]-first[i]-1
  • 删的是子串结束后的所有区间
    理解了第二种情况,其余两种情况就很好写了,分别是last[0]a.length()-first[b.length()-1]-1
#include<bits/stdc++.h>
using namespace std;
string a;
string b;
#define MAXN 200005
int ans=0;
int last[MAXN];//bi第一次出现在ai中的位置
int first[MAXN];//倒着的bi最后一次出现在ai中的位置
void pre()
{
    int cnt=0;
    for(int i=0;i<a.length();i++)
    {
        if(cnt==b.length()) break;
        if(a[i]==b[cnt])
        {
            first[cnt]=i;
            cnt++;
        }
    }
    cnt=b.length()-1;
    for(int i=a.length()-1;i>=0;i--)
    {
        if(cnt==-1) break;
        if(a[i]==b[cnt])
        {
            last[cnt]=i;
            cnt--;
        }
    }
//    for(int i=0;i<b.length();i++) cout<<first[i]<<" ";
//    cout<<endl;
//    for(int i=0;i<b.length();i++) cout<<last[i]<<" ";
//    cout<<endl;
}
void solve()
{
    int l1=last[0];
    int l2=a.length()-first[b.length()-1]-1;
    //cout<<l1<<" "<<l2<<endl;
    ans=max(l1,l2);
    for(int i=0;i<b.length()-1;i++)
    {
        ans=max(ans,last[i+1]-first[i]-1);
    }
    cout<<ans;
}
int main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    cin>>a>>b;
    pre();
    solve();
 
    return 0;
}

Codeforces 1205A 题目链接:点击这里传送

在这里插入图片描述
题意:
要求你用2n个数构成一个环,一条链的长度为n,一条链的权值为其所有元素的和,所有链的权值大小差距最多为1.问你能否构造出这个环出来
思路:
可以证明出来n是偶数时构造不了,但我不会证。
构造方法就是1的左边从上往下构造,1的右边下往上构造
1
N-1 N
N-2 …
… …
… 7
5 6
4 3
2

#include<bits/stdc++.h>
using namespace std;
int n;
#define MAXN 200005
int a[MAXN];
int main()
{
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	int cnt = 4;
	cin >> n;
	if (n % 2 == 0)
	{
		cout << "NO" << endl;
		return 0;
	}
	cout << "YES" << endl;
	a[1] = 1;
	a[n+1] = 2;
	a[n + 2] = 3;
	int p1 = 1; int p2 = n + 2;
	while (cnt <= 2 * n)
	{
		if (cnt + 2 <= 2 * n)
		{
			p1++;
			a[p1] = cnt;
			cnt++;
			p1++;
			a[p1] = cnt;
			cnt++;
		}
		
		if (cnt + 2 <= 2 * n)
		{
			p2++;
			a[p2] = cnt;
			cnt++;
			p2++;
			a[p2] = cnt;
			cnt++;
		}
		else if (cnt  ==2*n)
		{
			p2++;
			a[p2] = cnt;
			cnt+=2;
		}
		

	}
	for (int i = 1; i <= 2 * n; i++) cout << a[i] << " ";

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值