Codeforces Round #805 (Div. 3)(A-G)

Dashboard - Codeforces Round #805 (Div. 3) - CodeforcesCodeforces. Programming competitions and contests, programming communityhttps://codeforces.com/contest/1702

A. Round Down the Price

思路:直接枚举10的k次方即可,取符合条件的最大值.

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N =5e5+10,mod=998244353;
void solve()
{
    int n;
    cin>>n;
    for(int i=17;i>=0;i--)
    {
        int t=pow(10,i);
        if(n-t>=0)
        {
            cout<<n-t<<"\n";
            break;
        }
    }
}
signed main()
{
    int t;
    cin>>t;
    while(t--)
       solve();
    return 0;
}

B. Polycarp Writes a String from Memory

思路:我用了一个set去遍历这个字符串,如果当前的set的size在插入之后大于3,那么就说明这一段可以连续删除的一段可以删除了,就刷新set并且记录次数即可.

#include<bits/stdc++.h>
#define int long long
using namespace std;
void solve()
{
    int ans=0;
    string s;
    cin>>s;
    set<char>t;
    for(int i=0;i<s.size();i++)
    {
        t.insert(s[i]);
        if(t.size()>3)
        {
            ans++;
            t.clear();
            t.insert(s[i]);
        }
    }
    if(t.size())
        ans++;
    cout<<ans<<"\n";
}
signed main()
{
    int t;
    cin>>t;
    while(t--)
       solve();
    return 0;
}

C. Train and Queries

思路:Cwa3我差点就不想写摆烂了.我们发现数据小于1e9,但是点的个数只有1e5,就可以考虑用离散化,离散化每个站点的数据之后找到每个站点出现的最左边的位置和最右边的位置,每次对于询问去找询问的x点的最左边出现的位置是不是在y的最右边出现的位置的左边即可.

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N =5e5+10,mod=998244353;
struct node
{
    int a,id;
}edge[N];
int l[N];
int r[N];
map<int,int>dui;
bool cmp(node a,node b)
{
    return a.a<b.a;
}
bool cmp1(node a,node b)
{
    return a.id<b.id;
}
void solve()
{
    dui.clear();
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>edge[i].a;
        edge[i].id=i;
        l[i]=1e9;
        r[i]=-1e9;
    }
    sort(edge+1,edge+1+n,cmp);
    edge[0].a=-10;
    int tot=0;
    for(int i=1;i<=n;i++)
    {
        if(edge[i].a!=edge[i-1].a)
        {
            dui[edge[i].a]=++tot;
        }
        else
            dui[edge[i].a]=tot;
    }
    sort(edge+1,edge+1+n,cmp1);
    for(int i=1;i<=n;i++)
    {
        l[dui[edge[i].a]]=min(l[dui[edge[i].a]],i);
        r[dui[edge[i].a]]=max(r[dui[edge[i].a]],i);
    }
    int x,y;
    while(m--)
    {
        cin>>x>>y;
        if(dui[x]==0||dui[y]==0)
        {
            cout<<"NO\n";
            continue;
        }
        x=dui[x];
        y=dui[y];
        if(l[x]<r[y])
            cout<<"YES\n";
        else
            cout<<"NO\n";
    }
}
signed main()
{
    std::ios::sync_with_stdio(false);
    int t;
    cin>>t;
    while(t--)
       solve();
    return 0;
}

D. Not a Cheap String

思路,贪心删除即可.我们从大到小枚举26个字母,每次对于当前字符串的贡献如果大于n的话,就把当前选择的字母删除,删到符合条件即可(贪心思想,我每次删除贡献最大的字母就可以保证最最快的达到题目所要求的小于n的范围)

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N =5e5+10,mod=998244353;
int vis[202020];
void solve()
{
    int n,ans=0;
    int tot=0;
    string s;
    cin>>s>>n;
    memset(vis,0,(int)s.size()+100);
    for(int i=0;i<s.size();i++)
    {
        ans+=(s[i]-'a'+1);
    }
    if(ans<=n)
    {
        cout<<s<<"\n";
        return ;
    }
    for(int i=25;i>=0;i--)
    {
        char ch='a'+i;
        for(int j=0;j<s.size();j++)
        {
            if(s[j]==ch)
            {
                vis[j]=1;
                ans-=(i+1);
                if(ans<=n)
                {
                    for(int k=0;k<s.size();k++)
                    {
                        if(vis[k]==0)
                            cout<<s[k];
                    }
                    cout<<"\n";
                    return ;
                }
            }
        }
    }
    return ;
}
signed main()
{
    int t;
    cin>>t;
    while(t--)
       solve();
    return 0;
}

E. Split Into Two Sets

思路:我们先对牌组进行是否合法的判定.如果一张牌的两面相同,必定不合法,如果一个值在牌堆中出现不是2次,根据题意也肯定不合法.因为每一种值都有两张,那么我们对题目所给的牌正反面的二元关系作为依据,来进行并查集链接.每种值只有两张,那么并查集连接后肯定形成的是一条链(实际上是一个环)如果这个集合是偶数,我们就可以从中切开,从而保证达到题目要求

(可以自己去打表看环形的结构,肯定是符合条件的,比如:

1 2

2 3

3 4

4 5

5 6

6 1

满足之前的情况后每个集合肯定是这种形式,偶数环就肯定可以分成两等分来达到题目要求)

(群巨说可以二分图写)

 

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N =2e5+10,mod=998244353;
int s[N];
int find(int x)     				//查找结点 x的根结点 
{
    if(s[x] == x) return x;		//递归出口:x的上级为 x本身,即 x为根结点        
    return s[x] = find(s[x]);	//此代码相当于先找到根结点 rootx,然后s[x]=rootx 
}
map<int,int>ma;
map<int,int>m1;
void join(int x,int y)                     
{
    int fx=find(x), fy=find(y); 
    if(fx != fy) 
        s[fx]=fy;	//fy做fx的前驱结点
}
void solve()
{
    ma.clear();
    m1.clear();
    int n,x,y;
    cin>>n;
    for(int i=1;i<=n;i++)
        s[i]=i;
    int f=0;
    for(int i=1;i<=n;i++)
    {
        cin>>x>>y;
        m1[x]++;
        m1[y]++;
        if(x==y)
            f=1;
        if(s[x]!=s[y])
            join(x,y);
    }
    if(f==1)
    {
        cout<<"NO\n";
        return ;
    }
    for(int i=1;i<=n;i++)
        ma[find(i)]++;
    for(auto &x:ma)
    {
        if(x.second%2==1)
        {
            cout<<"NO\n";
            return ;
        }
    }
    if(m1.size()!=n)
    {
        cout<<"NO\n";
        return;
    }
    for(auto &x:m1)
    {
        if(x.second==1)
        {
            cout<<"NO\n";
            return ;
        }
    }
    cout<<"YES\n";
    return;
}
signed main()
{
    int t;
    cin>>t;
    while(t--)
       solve();
    return 0;
}

F. Equate Multisets

思路:a数组中,我们取他所有的数字的最大奇数因子进行记录,因为不含有奇数因子的数,我们都可以由b数组中任意一个数字变成1之后乘2乘出来.但是奇数因子不可以.而且b数组中的数字乘2的变化是消去不了这些奇数因子的,所以我们只需要看b的每个元素进行b[i]/2的计算,能不能得到a中记录的奇数因子,如果能得到,那么b[i]肯定就能通过题目所给的操作变成a数组中的数.

直接map记录跑一手就行了,最后判断map中记录的a的最大奇数因子的数量是不是>0的,>0就说明b数组消除a数组消除不干净,就是NO,反之YES.

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N =5e5+10,mod=998244353;
map<int,int>ma;
void solve()
{
    ma.clear();
    int n,x;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>x;
        while(x)
        {
            if(x%2==1)
            {
                ma[x]++;
                break;
            }
            x/=2;
        }
    }
    for(int i=1;i<=n;i++)
    {
        cin>>x;
        while(x)
        {
            if(ma[x]>0)
            {
                ma[x]--;
                break;
            }
            x/=2;
        }
    }
    for(auto &l:ma)
    {
        if(l.second>0)
        {
            cout<<"NO\n";
            return ;
        }
    }
    cout<<"YES\n";
    return;
}
signed main()
{
    int t;
    cin>>t;
    while(t--)
        solve();
    return 0;
}

G. Passable Paths (lca)

g1直接跑暴力.g2正解lca

按照题目的要求,这个点集肯定是在一条链上,不在一条链上肯定是NO,那么只需要求链子的两条端点,然后遍历点集,根据两个端点判断这个点是否在链子上即可;

链子的两个端点(p1,p2),其中一个端点(p1)一定是确认根节点后深度最大的那个点.另一个端点(p2)就不一定了,如果p2和p1的最近公共祖先(lca)就是p2的话,就说明两者在同一棵子树上,那么直接输出YES.

如果p1和p2的lca不等于p2,就说明两者在不同的子树.这时候只需要取除了p1深度最大的点即可.求出来p1,p2,就开始遍历点集,对每个点进行判断,只要满足当前的点和p1,p2两个点中任意一个点的lca是该点本身,和另一个点的lca是lca(p1,p2)(画图看一下就懂了).就可以保证这个点在这条链子上,反之不可以,直接输出NO.

#include<iostream>
#include<vector>
#include<cstdio>
using namespace std;
const int N=200100;
vector<int>to[N];
int si[N],deep[N],fa[N],top[N],son[N];
int a[N];
void dfs1(int x,int fa1)
{
	si[x]=1,deep[x]=deep[fa1]+1;
	son[x]=0,fa[x]=fa1;
	for(int i=0;i<to[x].size();i++)
	{
		int tt=to[x][i];
		if(tt==fa[x])continue;
		dfs1(tt,x);
		si[x]+=si[tt];
		if(si[son[x]]<si[tt])son[x]=tt;
	}
	return ;
}
void dfs2(int x,int tx)
{
	top[x]=tx;
	if(son[x]!=0)dfs2(son[x],top[x]);
	for(int i=0;i<to[x].size();i++)
		if(to[x][i]!=fa[x]&&to[x][i]!=son[x])
			dfs2(to[x][i],to[x][i]);
	return ;
}
int lca(int x,int y)
{
	while(top[x]!=top[y])
	{
		if(deep[top[x]]<deep[top[y]])swap(x,y);
		x=fa[top[x]];
	}
	return deep[x]<deep[y]? x:y;
}
void solve()
{
    int q,num,t,n,x,y;
    cin>>n;
    for(int i=1;i<=n;i++)
        to[i].clear();
    for(int i=1;i<n;i++)
    {
        cin>>x>>y;
        to[x].push_back(y);
        to[y].push_back(x);
    }
    dfs1(1,0);
    dfs2(1,0);
    cin>>q;
    while(q--)
    {
        int p1=1,p2=-1;
        cin>>num;
        for(int i=1;i<=num;i++)
        {
            cin>>a[i];
        }
        p1=a[1];
        for(int i=2;i<=num;i++)
            if(deep[p1]<deep[a[i]])
                p1=a[i];
        int f=0;
        for(int i=1;i<=num;i++)
        {
            if(a[i]!=p1)
            {
                if (lca(p1,a[i])!=a[i])
                {
                    f=1;
                    if(p2==-1||deep[a[i]]>deep[p2]) 
                        p2=a[i];
                }
            }
        }
        if(p2==-1)
        {
            cout<<"YES\n";
            continue;
        }
        int fff=0;
        int faa=lca(p1,p2);
        for(int i=1;i<=num;i++)
        {
            int fa1=lca(p1,a[i]);
            int fa2=lca(p2,a[i]);
            if((fa1==faa&&fa2==a[i])||(fa1==a[i]&&fa2==faa));
            else
                fff=1;
        }
        if(fff)
            cout<<"NO\n";
        else
            cout<<"YES\n";
    }
}
int main()
{
	solve();
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值