8.9个人训练总结

今晚CF鸽了,陪跑完AB就睡大觉去,傻逼学校那服务器我真服了,电费分几次交交不上的,直接没空调过了一晚。

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

在这里插入图片描述

题意:
给定两个二进制下的数字A和B,要求另A+B × \times × 2 k 2^k 2k的结果倒置后最小。求这时的k。
思路:
B × \times × 2 k 2^k 2k就类似于十进制的$\times$10,就是往后面加0。找到B串最后一次出现1的位置,通过加0的方式让B串这位上的1尽快和A串的1匹配上。(未匹配时为1,匹配后1+1=0,成功减小了字典序)
输出的就是这两位之间的差值

#include<bits/stdc++.h>
using namespace std;
string s1, s2;
int t;
int ans;
int pos;

int main()
{
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	cin >> t;
	while (t--)
	{
		ans = 0;
		cin >> s1 >> s2;
		int cnt = 0;
		for (int i = s2.length() - 1; i >= 0; i--)
		{
			if (s2[i] == '1') break;
			cnt++;
		}
		for (int i = s1.length()-1-cnt; i >= 0; i--)
		{
			if (s1[i] == '1') break;
			ans++;
		}
		cout << ans << endl;
	}
	return 0;
}

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

在这里插入图片描述

题意:
有上述的6种水管。1和2可以互相转化,3和4和5和6可以互相转化。要求从(1,1)走到(2,n),水管怎么摆看上面。按照给出的水管能不能到达终点?
思路:
如果都是1或2,状态不变。
如果都是3或4或5或6,状态改变(上下层变换)
如果当前在上层且上层为弯管道下层为直管道,管道泄漏。
如果当前在下层且上层为直管道下层为弯管道,管道泄漏。

#include<bits/stdc++.h>
using namespace std;
int t, n;
string s1, s2;
int f;//0表示第一层,1表示第二层
int main()
{
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	cin >> t;
	while (t--)
	{
		cin >> n;
		f = 0;
		cin >> s1 >> s2;
		for (int i = 0; i < n; i++)
		{
			if (s1[i] >= '3' && s2[i] <= '2'&&f==0 || s1[i] <= '2' && s2[i] >= '3'&&f==1)
			{
				f = 0;
				break;
			}
			if (s1[i] >= '3' && s2[i] >= '3')
			{
				f = 1-f;
			}
		}
		if (f) cout << "YES" << endl;
		else cout << "NO" << endl;
	}
	return 0;
}

Codeforces 1234B1/B2 题目链接:点击这里传送

在这里插入图片描述

题意:
一个人的手机发来了n条消息,但屏幕只能装下k条消息。如果这条消息的发送人以前发送的消息正出现在屏幕上,则忽略这条他现在发来的消息。否则,如果屏幕满了,把屏幕上最早的消息删了,再接着把这条消息放在屏幕的最后边。
思路:
用双向队列维护屏幕上的信息,用map判断重复性。

#include<bits/stdc++.h>
using namespace std;
#define MAXN 200005
int n,k;
int a[MAXN];
deque <int> q;
map <int,int> cnt;
int num;
int main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    cin>>n>>k;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        if(cnt[a[i]]==0)
        {
            cnt[a[i]]++;
            if(q.size()==k)
            {
                cnt[q.front()]--;
                q.pop_front();
                num--;
            }
            q.push_back(a[i]);
            num++;
        }
    }
    cout<<num<<endl;
    for(int i=num-1;i>=0;i--) cout<<q[i]<<" ";
    return 0;
}

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

在这里插入图片描述

题意:
一共有n个元素,两名选手每次可以再满足各自规定的前提下去任意多的元素。Alice只能去元素和为奇数,Bob只能取元素和为偶数。问谁最后胜利。
思路:
元素和为奇数。必然由奇数+偶数得到,Alice必胜。
元素和为偶数。可以奇数+奇数,可以偶数+偶数。换言之,只要有一个元素是奇数,就可以转变成Alice必胜。


好像写这破玩意有点用,之前写了一篇偶数+奇数=奇数思想做出来的题,印象很深刻,这题一分钟就做出来了。

#include<bits/stdc++.h>
using namespace std;
int f;
int n;
int temp;
int main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>temp;
        if(temp%2==1)
        {
            f=1;
            break;
        }
    }
    if(f) cout<<"First";
    else cout<<"Second";
    return 0;
}

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

在这里插入图片描述

题意:
有n个长度为 h i h_i hi的方块,你要从起点1走到终点n,一共有三种操作,有没有可能走到终点。

  • 往下凿方块并放入袋子(MC左键)
  • 往下垫方块(MC搭高台)
  • 如果 h i h_i hi h i + 1 h_{i+1} hi+1的差的绝对值小于k,直接跨过去。

思路:
每次都尽可能的往下凿方块,使袋子里的方块数量尽可能多,但得特判凿到地面的情况,这时就不能继续挖下去了。

#include<bits/stdc++.h>
using namespace std;
int t,n,m,k,f;
#define MAXN 105
int a[MAXN];
int main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    cin>>t;
    while(t--)
    {
        cin>>n>>m>>k;//几列方块,袋子里有几个方块,最多跨多少
        for(int i=1;i<=n;i++) cin>>a[i];
        f=1;
        if(n==1)
        {
            cout<<"YES"<<endl;
            continue;
        }
        for(int i=1;i<n;i++)
        {
            if(a[i]>=a[i+1])//尽可能移走最多方块
            {
                m+=min(a[i],(a[i]-a[i+1]+k));//这里卡了一次,不能贪心成负数
            }
            else if(a[i+1]-a[i]<=k)//尽可能的移走最多方块
            {
                m+=min(a[i],(k-(a[i+1]-a[i])));//一样
            }
            else if(a[i+1]-a[i]>k)//自己垫方块
            {
                if(m+a[i]+k<a[i+1])
                {
                    f=0;
                    break;
                }
                else
                {
                    m-=(a[i+1]-a[i]-k);
                }
            }
        }
        if(f) cout<<"YES"<<endl;
        else cout<<"NO"<<endl;
    }
    return 0;
}

Codeforces 1547E 题目链接:点击这里传送

在这里插入图片描述

题意:
n个空地上有m个空调,空调旁边的温度以公差为1向两列递增。求n个空地各自的温度(取最小值)
思路:
只考虑左边的,n跑过去;只考虑右边的,n跑过去。温度就是min( l i l_i li, r i r_i ri)。

#include<bits/stdc++.h>
using namespace std;
#define MAXN 300005
#define ll long long
ll l[MAXN];
ll r[MAXN];
ll a[MAXN];
ll pos[MAXN];
int t,n,m;
int main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    cin>>t;
    while(t--)
    {
        cin>>n>>m;
        memset(a,0x3f3f3f3f,sizeof(a));
        for(int i=1;i<=m;i++) cin>>pos[i];
        for(int i=1;i<=m;i++) cin>>a[pos[i]];
        ll p=999999999999;
        for(int i=1;i<=n;i++)
        {
            p=min(a[i],p+1);
            l[i]=p;
        }
        p=999999999999;
        for(int i=n;i>0;i--)
        {
            p=min(a[i],p+1);
            r[i]=p;
        }
        for(int i=1;i<=n;i++)
        {
            cout<<min(l[i],r[i])<<" ";
        }
        cout<<endl;
    }

    return 0;
}

Codeforces 1547F 题目链接:点击这里传送

在这里插入图片描述
题意:
一共n个数。每次操作后会产生一个新的数组,他的各个元素为:gcd( a 1 a_1 a1, a 2 a_2 a2),gcd( a 2 a_2 a2, a 3 a_3 a3)…gcd( a n a_n an, a 1 a_1 a1)
求需要几次这样的操作能使数组中的所有元素值相同。

思路:


最终的元素确定不下来,很难办,假设确定的话只能为1。操作就是 a i a_i ai/gcd( a 1 a_1 a1, a 2 a_2 a2,…, a n a_n an)


最后要求首尾gcd,可以拆环为链,把前n-1个数再复制到后面使得新数组的元素个数为2 × \times ×n-1


如果两个数互质,那么只需一次操作。
如果两个数有一个公因数2,一次操作后 a i a_i ai为2, a i + 1 a_{i+1} ai+1未知,需要原始的 a i + 2 a_{i+2} ai+2来确定。
对一次操作后的 a i + 1 a_{i+1} ai+1进行讨论

  • a i + 1 a_{i+1} ai+1不为2,此时原始值没有因子2,这样需要2次操作就可使 a i a_i ai变为1
  • a i + 1 a_{i+1} ai+1为2,此时原始值有因子2,经过第二轮操作后 a i a_i ai为2,要使 a i a_i ai为1要看接下来的 a i + 3 a_{i+3} ai+3, a i + 4 a_{i+4} ai+4,…,甚至 a i + n a_{i+n} ai+n

通过以上例子可以观察发现,若 a i a_i ai, a i + 1 a_{i+1} ai+1, a i + 2 a_{i+2} ai+2,…, a i + k a_{i+k} ai+k都有公因子2, a i + k + 1 a_{i+k+1} ai+k+1没有公因子2,那么需要k+1次操作才能消为1。
所以,策略就是找到第一次满足gcd( a i a_i ai, a i + 1 a_{i+1} ai+1, a i + 2 a_{i+2} ai+2,…, a i + k a_{i+k} ai+k)=1的序列,答案就是k。


区间gcd用线段树维护,找范围用二分(左端点 a i a_i ai遍历过去枚举,右端点用二分答案)。答案就是各个范围的最大值。

#include<bits/stdc++.h>
using namespace std;
#define MAXN 400005
#define ll long long
ll a[MAXN];
int t,n;
ll gcd(ll a,ll b)
{
    if(a<b) swap(a,b);
    ll temp;
    while(b)
    {
        temp=a%b;
        a=b;
        b=temp;
    }
    return a;
}
struct segtreenode
{
    int l,r;
    ll val;//区间内所有数的gcd
}tree[MAXN*4];
void pushup(int rt){tree[rt].val=gcd(tree[rt<<1].val,tree[rt<<1|1].val);}
void build(int rt,int l,int r)
{
    tree[rt].l=l;tree[rt].r=r;
    if(l==r)
    {
        tree[rt].val=a[l];
        return;
    }
    int mid=(l+r)>>1;
    if(mid>=l) build(rt<<1,l,mid);
    if(mid<r) build(rt<<1|1,mid+1,r);
    pushup(rt);
}
ll query(int rt,int l,int r)
{
    if(tree[rt].l>r||tree[rt].r<l) return 0;
    if(tree[rt].l>=l&&tree[rt].r<=r) return tree[rt].val;
    return gcd(query(rt<<1,l,r),query(rt<<1|1,l,r));
}
int main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    cin>>t;
    while(t--)
    {
        cin>>n;
        for(int i=1;i<=n;i++) cin>>a[i];
        for(int i=1;i<n;i++) a[i+n]=a[i];
        ll pub=a[1];
        for(int i=1;i<n;i++) pub=gcd(pub,a[i+1]);
        if(pub!=1)
        {
            for(int i=1;i<=2*n-1;i++) a[i]=a[i]/pub;
        }
       //for(int i=1;i<=n;i++) cout<<a[i]<<" ";
      // cout<<endl;
        build(1,1,2*n-1);
        int ans=0;
        for(int i=1;i<=n;i++)
        {
            if(a[i]==1) continue;
            int l=i+1;int r=i+n;
            while(l<r)
            {
                int mid=(l+r)/2;
                if(query(1,i,mid)==1)
                {
                    r=mid;
                }
                else
                {
                    l=mid+1;
                }
            }
            ans=max(ans,r-i);
        }
        cout<<ans<<endl;
    }
    return 0;
}

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

在这里插入图片描述

题意:
有一个拳击手,他可以选择数组中最左或最右的元素将其击碎。问最少需要几次这样的过程能把数组中的最大值和最小值都击碎了。
思路:
分类讨论。击碎同一边分两种,左右向中间击碎也可以分两种。

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

int main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    cin>>t;
    while(t--)
    {
        cin>>n;
        for(int i=1;i<=n;i++)
        {
            cin>>a[i];
            if(a[i]==1) mini=i;
            if(a[i]==n) maxi=i;
        }
       int ans1=max(mini,maxi);
       int ans2=n-min(mini,maxi)+1;
       int ans3=mini+n-maxi+1;
       int ans4=maxi+n-mini+1;
       int ans=min(ans1,ans2);
       ans=min(ans,ans3);
       ans=min(ans,ans4);
       cout<<ans<<endl;
    }

    return 0;

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值