Codeforces Round 805 (Div. 3)

目录

A.Round Down the Price

B. Polycarp Writes a String from Memory

C. Train and Queries

D. Not a Cheap String

E. Split Into Two Sets

F. Equate Multisets

G1&&G2. Passable Paths (hard version)


A.Round Down the Price

按题意找到比当前数小的最大的10的整数幂的数,然后差值即为答案,我这里写复杂了,用的字符串,记得要考虑前导0

代码如下:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <map>
#include <cmath>

using namespace std;

typedef long long ll;
typedef pair<int, int> pii;

const int N = 1e5 + 10;

int main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    
    int t;
    cin>>t;
    while(t--)
    {
        string s;cin>>s;
        string s1;
        if(s[0]=='1')s1=s.substr(1);
        else 
        {
            int ch=(s[0]-'1');
            s1=to_string(ch),s1+=s.substr(1);
        }
        int cnt=0;
        for(int i=0;i<s1.size();i++)if(s1[i]=='0')cnt++;
        if(s1.size()==cnt)cout<<0<<"\n";
        else 
        {
            for(int i=0;i<s1.size();i++)
                if(s1[i]!='0')
                {
                    cout<<s1.substr(i)<<"\n";
                    break;
                }
        }
    }
    return 0;
}

B. Polycarp Writes a String from Memory

题目要求我们每次只能删除3种字母,用map记录下来即可

代码如下:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <map>
#include <cmath>

using namespace std;

typedef long long ll;
typedef pair<int, int> pii;

const int N = 1e5 + 10;

int main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    
    int t;cin>>t;
    while(t--)
    {
        string str;cin>>str;
        map<char,int>mp;
        int cnt=0,day=0;
        for(int i=0;i<str.size();i++)
        {
            if(!mp.count(str[i]))
            {
                cnt++;
                mp[str[i]]++;
            }
            if(cnt==3)
            {
                int r=i;
                while(r<str.size()&&mp[str[r]])r++;
                i=max(i,r-1);
                cnt=0;
                mp.clear();day++;
            }
        }
        if(!mp.empty())day++;
        cout<<day<<"\n";
    }
    return 0;
}

C. Train and Queries

题目大意:给定一个序列,然后每次询问给定两个数x,y,问序列中x后面是否有y。

思路:因为一个数可能出现多次,因此我们要存下来每个数出现的位置,并且记录下来每个数第一次出现的位置,最后二分查找即可。最后要注意一下,因为数可能很大,所以要离散化

代码如下:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <map>
#include <cmath>

using namespace std;

typedef long long ll;
typedef pair<int, int> pii;

const int N = 1e5 + 10;

int main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    
    int t;cin>>t;
    while(t--)
    {
        int n,q;cin>>n>>q;
        vector<int>a(n);
        vector<int>p[n];
        map<int,int>mp,mp1;
        
        int idx=0;
        for(int i=0;i<n;i++)
        {
            cin>>a[i];
            if(!mp1.count(a[i]))mp1[a[i]]=i;//记录下来每个数第一次出现的位置

            if(!mp.count(a[i]))mp[a[i]]=idx++;//离散化,给每个数编号
            p[mp[a[i]]].push_back(i);//记录下来每个数出现的位置
        }
        while(q--)
        {
            int x,y;
            cin>>x>>y;
            if(!mp.count(x)||!mp.count(y))
            {
                cout<<"NO"<<"\n";
                continue;
            }
            int id=mp1[x];
            auto it=lower_bound(p[mp[y]].begin(),p[mp[y]].end(),id);
            if(it!=p[mp[y]].end())cout<<"YES"<<"\n";
            else cout<<"NO"<<"\n";
        }

    }
    return 0;
}

D. Not a Cheap String

题目大意:给定一个字符串,字符串的权值等于每个字母在字母表中的位置的总和,例如a的权值是1,b的权值是2 ,字符串ab的权值就是1+2=3.再给定一个整数k,要求删除尽量少的字符,使得字符串的权值小于等于整数k。

思路:我们可以记录下来每一个字符出现的位置,依次遍历删除字符z到a,删除后的权值是否满足条件,删除的位置标记下来即可。

代码如下:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <map>
#include <cmath>

using namespace std;

typedef long long ll;
typedef pair<int, int> pii;

const int N = 1e5 + 10;

int main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    
    int t;cin>>t;
    while(t--)
    {
        string s;cin>>s;
        int q;cin>>q;

        int len=s.size();
        vector<bool>st(len,0),st1(26,0);
        vector<int>v[26];
        int  sum=0;
        for(int i=0;i<len;i++)
        {
            sum+=(s[i]-'a')+1;
            v[s[i]-'a'].push_back(i);
        }
        if(sum<=q)
        {
            cout<<s<<"\n";
            continue;
        }
        for(int i=25;i>=0;i--)
        {
            if(sum<=q)break;
            if(v[i].size())
            {
                if(sum-(v[i].size()*(i+1))>=q)sum-=(v[i].size()*(i+1)),st1[i]=1;//表示字符(‘a'+i)整个都可以删去
                else
                {
                    for(int j=0;j<v[i].size();j++)
                    {
                        sum-=(i+1);
                        st[v[i][j]]=1;//表示字符('a'+i)的第j个位置可以删去
                        if(sum<=q)break;
                    }
                }
            }
        }
        for(int i=0;i<len;i++)
        {
            if(st1[s[i]-'a']||st[i])continue;
            cout<<s[i];
        }
        cout<<"\n";
    }
    return 0;
}

E. Split Into Two Sets

题目大意:给定n个多米诺骨牌,每张骨牌上面有两个数,要求将所有的骨牌分成两堆,使得每堆中的数字不重复。

思路:这题可以用二分图匹配做,如果一个数出现了三次及以上,那么一定不满足题意,如果一张骨牌上的两个数一样,那么也一定不满足题意。如果一个数只出现过一次,那我们就不用管它,如果一个数出现了两次,那么我就用一条边连接这两个骨牌,表示这两张牌不能分到一堆去,最后做一个二分图匹配,如果能成功匹配,说明满足题意,否则不满足

代码如下:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <map>
#include <cmath>
#include <set>

using namespace std;

typedef long long ll;
typedef pair<int, int> pii;

const int N = 2e5 + 10,M=2*N;

int n;
int h[N],e[M],ne[M],idx;
int c[N];
vector<int>st[N];

bool dfs(int u,int color)
{
    c[u]=color;
    for(int i=h[u];i!=-1;i=ne[i])
    {
        int j=e[i];
        
        if(!c[j])
        {
            if(!dfs(j,3-color))return false;
        }
        else if(c[j]==color)return false;
    }
    return true;
}
void add(int a,int b)
{
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
int main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    
    int t;cin>>t;
    while(t--)
    {
        cin>>n;
        for(int i=0;i<=n;i++)h[i]=-1,c[i]=0,st[i].clear();
        idx=0;
        
        bool ok=1;
        for(int i=1;i<=n;i++)
        {
            int a,b;
            cin>>a>>b;
            if(a==b)ok=0;

            st[a].push_back(i);st[b].push_back(i);
            if(st[a].size()>2||st[b].size()>2)ok=0;
        }        

        if(!ok)
        {
            cout<<"NO"<<"\n";
            continue;
        }

        set<pii>s;
        for(int i=1;i<=n;i++)
        {
           if(st[i].size()==2)
           {
                add(st[i][0],st[i][1]);
                add(st[i][1],st[i][0]);
           }
        }

        for(int i=1;i<=n;i++)
        {
            if(!c[i])
            {
                if(!dfs(i,1))
                {
                    ok=0;
                    break;
                }
            }
        }
        if(ok)cout<<"YES"<<"\n";
        else cout<<"NO"<<"\n";
    }
    return 0;
}

F. Equate Multisets

贪心

题目大意:给定两个数组a和b,可以在b数组进行两种操作,一种是将b中的任意一个元素变大两倍即×2,另一种是/2,可以进行任意次数的操作,判断能否操作使得a和b相等

思路:其实我们可以用操作1操作数组a,因为假设a=10,b=5,a/2==b*2,因此我们可以将a数组中所有能整除2的数一直整除到不能整除,直至变成一个奇数,这样子a中只有奇数了,那么对于b数组*2的操作就没用了,因为一个数*2是偶数,因此,对于b中的每个数,我们可以首先判断在a中是否有,如果没有的话,我们可以一直整除,直至到1,判断是否a中存在整个数,如果不存在说明不满足题意,如果所有数都可以操作后使得a和b相等, 那么就满足题意

代码如下:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <map>
#include <cmath>

using namespace std;

typedef long long ll;
typedef pair<int, int> pii;

const int N = 1e5 + 10;

int main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    
    int t;
    cin>>t;
    while(t--)
    {
        int n;cin>>n;

        bool ok=1;
        map<int,int>mp;
        for(int i=0;i<n;i++)
        {
            int x;cin>>x;
            while(x%2==0)x/=2;
            mp[x]++;
        }
        for(int i=0;i<n;i++)
        {
            int x;cin>>x;
            while(x%2==0)x/=2;
            while(x!=1&&mp[x]==0)x/=2;
            if(mp[x]==0)ok=0;
            else mp[x]--;
        }
        if(ok)cout<<"YES"<<"\n";
        else cout<<"NO"<<"\n";
    }
    return 0;
}

G1&&G2. Passable Paths (hard version)

lca

题目大意:给定一棵树,然后给定q个询问,对于每个询问,给定k个数,判断这k个数在给定的树中是否在一条链上。

思路:对于给定的k个数,我们首先找到两个端点t1,t2,然后找到两个端点的lca(t1,t2)=r,那么对于给定的k个数中的其他数p,要么在t1到r的路径上,要么在t2到r的路径上,如果不在其中任何一条路径上,说明p跟t1和t2不在一条链上,那么如果找到这两个端点呢,对于t1,我们可以让其等于深度最深的节点,对于t2,我们可以让其为跟t1不在一个子树且深度最深的节点,即要满足lca(t1,t2)!=t2,如果找不到满足条件的t2,说明在一条链上

代码如下:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <map>
#include <cmath>

using namespace std;

typedef long long ll;
typedef pair<int, int> pii;

const int N = 2e5 + 10,M=2*N;

int n;
int h[N],e[M],ne[M],idx;
int depth[N],fa[N][20];
int q[N],p[N];

void add(int a,int b)
{
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void bfs()
{
    int hh=0,tt=0;
    q[0]=1;depth[0]=0,depth[1]=1;
    while(hh<=tt)
    {
        int t=q[hh++];
        for(int i=h[t];i!=-1;i=ne[i])
        {
            int j=e[i];
            if(depth[j]>depth[t]+1)
            {
                depth[j]=depth[t]+1;
                q[++tt]=j;
                fa[j][0]=t;
                for(int k=1;k<=18;k++)
                    fa[j][k]=fa[fa[j][k-1]][k-1];
            }
        }
    }
}
int lca(int a,int b)
{
    if(depth[a]<depth[b])swap(a,b);
    for(int k=18;k>=0;k--)
        if(depth[fa[a][k]]>=depth[b])
            a=fa[a][k];
    if(a==b)return a;
    for(int k=18;k>=0;k--)
        if(fa[a][k]!=fa[b][k])
        {
            a=fa[a][k];
            b=fa[b][k];
        }
    return fa[a][0];
}
int main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    
    cin>>n;
    for(int i=0;i<=n;i++)h[i]=-1,depth[i]=0x3f3f3f3f;
    idx=0;
    for(int i=0;i<n-1;i++)
    {
        int a,b;
        cin>>a>>b;
        add(a,b);add(b,a);
    }
    bfs();

    int q;cin>>q;
    while(q--)
    {
        int k;cin>>k;
        for(int i=0;i<k;i++)cin>>p[i];

        int t1=p[0],t2=-1;
        for(int i=1;i<k;i++)
            if(depth[p[i]]>=depth[t1])t1=p[i];
        for(int i=0;i<k;i++)
        {
            if(p[i]!=t1&&lca(p[i],t1)!=p[i])
            {
                if((t2==-1)||(depth[p[i]]>=depth[t2]))
                    t2=p[i];
            }
        }

        if(t2==-1)
        {
            cout<<"YES"<<"\n";
            continue;
        }
        bool ok=1;
        for(int i=0;i<k;i++)
        {
            int x=p[i];
            int r=lca(t1,t2);
            int l1=lca(p[i],t1);
            int r1=lca(p[i],t2);
            if(!((l1==p[i]&&r1==r)||(l1==r&&r1==p[i])))
            {
                ok=0;
                break;
            }
        }
        if(ok)cout<<"YES"<<"\n";
        else cout<<"NO"<<"\n";
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值