Codeforces Round #698 (Div. 2)

A:Nezzar and Colorful Balls

  给你一个序列,你可以选择一个严格递增序列划分成一组,问你最少只需要划分几组。典型的贪心问题,划分成一组时尽可能地把能加入的元素都加入当前的序列中。

#include<bits/stdc++.h>
#define close ios::sync_with_stdio(false)
using namespace std;
int a[110],vis[110];
int main()
{
    close;
    int T;cin>>T;
    while(T--)
    {
        int n;cin>>n;
        for(int i=1;i<=n;++i) cin>>a[i];
        memset(vis,0,sizeof(vis));
        int ans=0;
        while(1)
        {
            bool ok=false;int cur=0;
            for(int i=1;i<=n;++i)
                if(!vis[i] && a[i]>cur) cur=a[i],vis[i]=1,ok=true;
            if(ok) ans++;
            else break;
        }
        cout<<ans<<endl;
    }
}

B:Nezzar and Lucky Number

  给定一个数 d d d,当 a i a_i ai在他的十进制表示中, d d d出现了至少一次,这个数就是幸运数字。而题目要求的是求解对于给定的元素 x x x,这个数是否本身是幸运数字或者可以表示为一系列幸运数字的和。
  我们首先要发现这样一个规律:①当 x ≥ 10 ∗ d x\geq10*d x10d时一定是“YES”。因为当两个数的十位数不同时,他们的差最小是2,最大是18。 [ d ∗ 10 , d ∗ 10 + 9 ] [d*10,d*10+9] [d10,d10+9]区间的数是幸运数字,而大于这些数的时候,总能通过这些幸运数字加上若干个 d d d形成(因为上述的幸运区间的跨度是10,恒大于 d d d,因此能做到全覆盖)。②而当 x < 10 ∗ d x<10*d x<10d的时候情况就有些许复杂,造成该现象的原因是一些个位出现了 d d d的数字造成构成的数字不是 d d d的倍数(例如 d = 3 d=3 d=3时, 16 = 13 + 3 16=13+3 16=13+3)我们暴力判断即可。

#include<bits/stdc++.h>
#define close ios::sync_with_stdio(false)
using namespace std;
bool Judge(int x,int d)
{
    if(x<=0) return false;
    while(x){if(x%10==d) return true;return false;}
}
bool Solve(int x,int d)
{
    if(x>=d*10 || Judge(x,d)) return true;
    while(x>0){x-=d;if(Judge(x,d)) return true;}
    return  false;
}
int main()
{
    close;int T;cin>>T;
    while(T--)
    {
        int q,d;cin>>q>>d;
        for(int i=1;i<=q;++i) 
        {
            int x;cin>>x;
            if(Solve(x,d)) cout<<"YES"<<endl;
            else cout<<"NO"<<endl;
        }
    }
}

C:Nezzar and Symmetric Array

  给定序列 d d d,满足 d i = ∑ j = 1 2 n ∣ a i − a j ∣ d_i=\sum_{j=1}^{2n} |a_i-a_j| di=j=12naiaj;而序列 a a a满足:① 2 n 2n 2n个元素互不相同;②对于任意的元素 a i a_i ai,一定在原序列中存在 − a i -a_i ai.问这样的序列 a a a是否存在。
  我们可以通过计算比较出序列 d d d的值和点坐标的关系。例如下面的八个点:
在这里插入图片描述
八个点关于坐标原点两两对称,我们计算后面四个点的 d i d_i di,分别是: d 1 = ( x 1 + x 2 + x 3 + x 4 ) ∗ 2 d_1=(x_1+x_2+x_3+x_4)*2 d1=(x1+x2+x3+x4)2 d 2 = ( x 2 + x 2 + x 3 + x 4 ) ∗ 2 d_2=(x_2+x_2+x_3+x_4)*2 d2=(x2+x2+x3+x4)2 d 3 = ( x 3 + x 3 + x 3 + x 4 ) ∗ 2 d_3=(x_3+x_3+x_3+x_4)*2 d3=(x3+x3+x3+x4)2 d 4 = ( x 4 + x 4 + x 4 + x 4 ) ∗ 2 d_4=(x_4+x_4+x_4+x_4)*2 d4=(x4+x4+x4+x4)2很容易发现可以从最后一个方程开始,依次倒推去解 x 4 , x 3 , x 2 , x 1 x_4,x_3,x_2,x_1 x4,x3,x2,x1,只要能有正整数解,同时各个解互不相同,说明这个答案是成立的,即我们能找到这样的序列 a a a.

#include<bits/stdc++.h>
#define close ios::sync_with_stdio(false)
using namespace std;
const int maxn=2e5+100;
typedef long long ll;
ll a[maxn];int n;
bool solve()
{
    sort(a+1,a+2*n+1);
    for(int i=1;i<=2*n;i+=2)
        if(a[i]&1 || a[i]!=a[i+1]) return false;
    if(a[2*n]%(2*n)!=0) return false;
    ll last=a[2*n]/(n*2),sum=last;
    if(last==0) return false;
    for(int i=n-1;i>0;--i)
    {
        if((a[2*i]-sum*2)%(2*i)!=0) return false;
        ll cur=(a[2*i]-sum*2)/(2*i);
        if(cur>=last || cur<=0) return false;
        last=cur;sum+=cur;
    }
    return true;
}
int main()
{
    close;int T;cin>>T;
    while(T--)
    {
        cin>>n;
        for(int i=1;i<=2*n;++i)  cin>>a[i];
        if(solve()) cout<<"YES"<<endl;
        else cout<<"NO"<<endl;
    }
}

D:Nezzar and Board

  题目的意思是给定 n n n个不同的数 x i x_i xi,能否通过不断选取 x , y x,y x,y,并加入新的元素 2 x − y 2x-y 2xy的方式,最后构造出 k k k.
  这个题本质上是在考察扩展欧几里得算法:对于 a , b a,b a,b来说,一定能找到 q , r q,r q,r使得 a = q ∗ b + r a=q*b+r a=qb+r成立。而求解最大公因数就是采用辗转相除的方式,算法的终点就是 a = = g c d , b = = 0 a==gcd,b==0 a==gcd,b==0.得到0对于这个题是毫无意义的,而gcd应该是我们能够得到的最小的数。而 2 x − y 2x-y 2xy本质上就是在模拟辗转相除,例如令 y = q x + r y=qx+r y=qx+r,那么经过不断地变化我们能够得到 r ( q 为 偶 数 ) 或 者 − x + r ( q 为 奇 数 ) r(q为偶数)或者-x+r(q为奇数) r(q)x+r(q),而前面的 x x x有或者无都不影响gcd的求解,即 g c d ( − x + r , x ) = = g c d ( r , x ) gcd(-x+r,x)==gcd(r,x) gcd(x+r,x)==gcd(r,x)。因此,我们只要找到上述所有相邻元素的差值,并求解gcd,就是在求解变换的最小步长。只要 ( k − a [ 1 ] ) % s t e p m i n = = 0 (k-a[1])\%step_{min}==0 (ka[1])%stepmin==0,则该变换能够做到。

#include<bits/stdc++.h>
#define close ios::sync_with_stdio(false)
using namespace std;
typedef long long ll;
const int maxn=2e5+100;
ll a[maxn];
int main()
{
    close;int T;cin>>T;
    while(T--)
    {
        ll n,k;cin>>n>>k;
        for(int i=1;i<=n;++i) cin>>a[i];
        sort(a+1,a+n+1);
        ll min_minus=0;
        for(int i=1;i<n;++i) min_minus=__gcd(min_minus,a[i+1]-a[i]);
        if((k-a[1])%min_minus==0) cout<<"YES"<<endl;
        else cout<<"NO"<<endl;
    }
}

E:Nezzar and Binary String

  题目的意思是给定起始字符串和终止字符串,然后给出q组区间,每次先比较该区间是否都是相同的数字,然后可以对该区间进行修改,但修改的个数必须严格小于长度的一半,问最终能否达到终止字符串。
  这个题的关键在于反向思维,给你终止字符串,反向给你询问区间,你先修改这个区间使其变成相同的数字,然后再由对方判断,最终如果能到达起始字符串就输出“YES”,反向思维的好处是在于这种情况下做出的修改的方式是唯一的。需要注意一个细节,但询问的区间长度是偶数时,而恰好0,1的数量各占一半,这种情况没有办法作出修改,一定无法让区间全为一种数字,直接输出“NO”。
  区间覆盖成一种数字可以采用线段树的方式,最终的复杂度是 O ( ( n + q ) l o g n ) O((n+q)logn) O((n+q)logn).

#include<bits/stdc++.h>
#define close ios::sync_with_stdio(false)
using namespace std;
const int maxn=2e5+100;
int before[maxn],after[maxn],tree[maxn*4],mark[maxn*4],n,q;
char be[maxn],af[maxn];
struct Rec{
    int l,r;
}rec[maxn];
void push_down(int p,int len)
{ 
	if(mark[p]==-1) return;
    mark[2*p]=mark[p];mark[2*p+1]=mark[p];
	tree[2*p]=mark[p]*(len-len/2);
	tree[2*p+1]=mark[p]*(len/2);
	mark[p]=-1;
}
void build(int l=1,int r=n,int p=1)
{
	if(l==r) tree[p]=after[l];
	else{
		int mid=(l+r)/2;
		build(l,mid,2*p);
		build(mid+1,r,2*p+1);
		tree[p]=tree[p*2]+tree[p*2+1];
	}
}
void update(int l,int r,int d,int p=1,int cl=1,int cr=n)
{
	if(l>cr || r<cl) return;
	if(l<=cl && cr<=r) 
	{
		tree[p]=d*(cr-cl+1);
		if(cr>cl) mark[p]=d;
	}  
	else{
		int mid=(cl+cr)/2;
		push_down(p,cr-cl+1);
		update(l,r,d,p*2,cl,mid);
		update(l,r,d,p*2+1,mid+1,cr);
		tree[p]=tree[p*2]+tree[p*2+1]; 
	} 
}
int query(int l,int r,int p=1,int cl=1,int cr=n)
{
	if(l>cr || r<cl) return 0;
	if(l<=cl && cr<=r) return tree[p];
	else{
		int mid=(cl+cr)/2;
		push_down(p,cr-cl+1);
		return query(l,r,2*p,cl,mid)+query(l,r,2*p+1,mid+1,cr);
	}
}
bool solve()
{
    scanf("%d%d",&n,&q);
    scanf("%s",be);scanf("%s",af);
    for(int i=0;i<n;++i) before[i+1]=be[i]-'0',after[i+1]=af[i]-'0';
    for(int i=0;i<=4*n+10;++i) mark[i]=-1;
    build();
    for(int i=1;i<=q;++i) scanf("%d%d",&rec[i].l,&rec[i].r);
    for(int i=q;i>0;--i)
    {
        int cur=query(rec[i].l,rec[i].r),len=rec[i].r-rec[i].l+1;
        if(len%2==0){
            if(cur*2==len) return false;
            if(cur*2>len) update(rec[i].l,rec[i].r,1);
            else update(rec[i].l,rec[i].r,0);
        }
        else{
            if(cur*2>len) update(rec[i].l,rec[i].r,1);
            else update(rec[i].l,rec[i].r,0);
        }
    }
    for(int i=1;i<=n;++i)
        if(query(i,i)!=before[i]) return false;
    return true;
}
int main()
{
    close;int T;scanf("%d",&T);
    while(T--)
    {
        if(solve()) printf("YES\n");
        else printf("NO\n");
    }
}

F:Nezzar and Nice Beatmap

  题目要求给出一个序列,使得序列中任意三个连续的顶点 A , B , C A,B,C A,B,C,构成的三角形(可以退化成一条边)中, ∠ B < 9 0 ∘ \angle B<90^\circ B<90
  这道题可以理解成一个三角形的定理:三角形中最多有一个钝角或者直角,而且一定是最长边所对应的角。因此,我们可以任选一个点作为起点,每轮迭代都找一条最长的边,那么这条边与其他边所夹的角一定是锐角。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=5e3+100;
struct Point{
    ll x,y;
}P[maxn];
int vis[maxn];
double GetDistance(int i,int j) {return sqrt((P[i].x-P[j].x)*(P[i].x-P[j].x)+(P[i].y-P[j].y)*(P[i].y-P[j].y));}
int main()
{
    int n;scanf("%d",&n);
    for(int i=1;i<=n;++i) scanf("%lld%lld",&P[i].x,&P[i].y);
    memset(vis,0,sizeof(vis));
    printf("1");vis[1]=1;
    int cur_id=1;
    for(int i=2;i<=n;++i)
    {
        double max_dis=0.0;int next_id;
        for(int i=1;i<=n;++i)
        {
            if(vis[i]) continue;
            double cur_dis=GetDistance(cur_id,i);
            if(cur_dis>max_dis) max_dis=cur_dis,next_id=i;
        }
        printf(" %d",next_id);
        cur_id=next_id;vis[next_id]=1;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值