Codeforces Round #764 (Div. 3)

Codeforces Round #764 (Div. 3)

VP直播回放:https://www.bilibili.com/video/BV1GT4y127WQ (A-D)

A - Plus One on the Subset

讲了一堆,最后只要最大-最小就行了。

/* Author: cyanine_farewell
 * Time: 2022-01-19 19:30:03
**/
#include<bits/stdc++.h>
#define pb push_back
#define ll long long
#define fast ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
const int maxn = 60;
int main()
{
    fast;
    int T;
    cin>>T;
    while(T--)
    {
        int n;
        cin>>n;
        int x;
        int maxx=0;
        int minn=1e9;
        for(int i=1;i<=n;i++)
        {
            cin>>x;
            maxx=max(maxx,x);
            minn=min(minn,x);
        }
        cout<<maxx-minn<<'\n';
    }
    return 0;
}

B - Make AP

题意:仅进行一次如下操作:选择\(a,b,c\)中的一个数,让其乘上一个正整数\(m\),最终\(a,b,c\)形成等差数列。

思路:由于是等差数列,所以最终的\(a,b,c\)满足\(a+c=2\cdot b\)。我们分别讨论改变\(a,b,c\)形成等差数列的可行性。如果改变\(a\),我们需要考虑\((2\cdot b-c)%a\)是否为\(0\),同时商是否大于\(0\);改变\(c\)类似。改变\(b\)需要多考虑\((a+c)\)是否能被\(2\)整除。

/* Author: cyanine_farewell
 * Time: 2022-01-19 19:30:03
**/
#include<bits/stdc++.h>
#define pb push_back
#define ll long long
#define fast ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
const int maxn = 100010;
int main()
{
    fast;
    int T;
    cin>>T;
    while(T--)
    {
        bool flag=0;
        int a,b,c;
        int ma,mb,mc;
        cin>>a>>b>>c;
        ma=b-(c-b);
        if(ma%a==0 && ma/a>0)
        {
            cout<<"YES\n";
            continue;
        }
        mc=b+(b-a);
        if(mc%c==0 && mc/c>0)
        {
            cout<<"YES\n";
            continue;
        }
        if((c+a)%2==0)
        {
            mb=(a+c)/2;
            if(mb%b==0 && mb/b>0)
            {
                cout<<"YES\n";
                continue;
            }
        }
        cout<<"NO\n";
    }
    return 0;
}

C - Division by Two and Permutation

题意:给出\(n\)个数,重复如下操作:选择其中一个数\(x\)进行\(\lfloor x/2 \rfloor\)。问\(n\)个数能否构成\(1-n\)的排列。

思路:例如想要获得数字\(2\),它可以从\(4\)\(5\)经过操作得来。可以发现较小的数字可以从多个较大的数字经过操作得到。因此,我们先将所有的数的范围控制在\(1-n\),统计各个数的个数\(f[i]\),从大到小遍历,如果\(f[i]>1\),那么就可以把多的数字进行操作,状态转移方程为\(f[i/2]+=f[i]-1\),最后判断\(1-n\)范围内所有的\(f[i]\)是否均为\(1\)

/* Author: cyanine_farewell
 * Time: 2022-01-19 19:30:03
**/
#include<bits/stdc++.h>
#define pb push_back
#define ll long long
#define fast ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
const int maxn = 60;
int f[maxn];
int main()
{
    fast;
    int T;
    cin>>T;
    while(T--)
    {
        int x;
        int n;
        cin>>n;
        for(int i=1;i<=n;i++)
        {
            f[i]=0;
        }
        for(int i=1;i<=n;i++)
        {
            cin>>x;
            while(x>n) x/=2;
            f[x]++;
        }
        for(int i=n;i>=1;i--)
        {
            if(f[i]>1)
            {
                f[i/2]+=f[i]-1;
            }
        }
        bool ans=1;
        for(int i=1;i<=n;i++)
        {
            if(f[i]==0)
            {
                ans=0;
                break;
            }
        }
        if(ans) cout<<"YES\n";
        else cout<<"NO\n";
    }
    return 0;
}

D - Palindromes Coloring

题意:给出一个字符串和\(k\)种颜色,对字符串中的字符涂色(必须\(k\)种颜色全部用到,所有字符不需要都涂上色),使得相同颜色的字符在交换顺序后能够形成回文串,问形成的最小回文串的最大长度。

思路:若想构成回文串,至多一种字符的个数为奇数,其余均为偶数。首先统计每个字符的个数,如果个数为奇数,就将多出来的那一个统计入\(rest\),使得每种字符的统计个数均为偶数,设这些字符个数总数为\(x\),那么可以构成\(k\)个长度为\(x/k\)的回文串与\(x\%k\)个剩余字符。如果\(x/k\)为奇数,那么必然无法加上新的字符,答案即为\(x/k\);如果\(x/k\)为偶数,我们可以对其中的回文串加上新的字符,那么只需要讨论\(x\%k+rest\)\(k\)的大小关系。如果\(x\%k+rest \geq k\),答案为\(x/k+1\),否则为\(x/k\)

/* Author: cyanine_farewell
 * Time: 2022-01-19 19:30:03
**/
#include<bits/stdc++.h>
#define pb push_back
#define ll long long
#define fast ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
const int maxn = 100010;
int n,k;
string s;
int d[26]={0};
int main()
{
    fast;
    int T;
    cin>>T;
    while(T--)
    {
        int rest=0;
        int sum=0;
        cin>>n>>k;
        cin>>s;
        for(int i=0;i<26;i++)
        {
            d[i]=0;
        }
        for(int i=0;i<n;i++)
        {
            d[s[i]-97]++;
        }
        for(int i=0;i<26;i++)
        {
            rest+=d[i]%2;
            d[i]=d[i]/2*2;
            sum+=d[i];
        }
        int ans=sum/k;
        int r=sum%k;
        if(ans%2)
        {
            ans=ans;
        }
        else
        {
            if(r+rest>=k)
            {
                ans++;
            }
        }
        cout<<ans<<'\n';
    }
    return 0;
}

E - Masha-forgetful

题意:给出\(n\)个字符串,给出一种组合方式,使得这\(n\)个字符串的若干个子串可以构成指定的字符串。

思路:因为不需要考虑最少子串,因此我们把所有的字符串拆成长度为\(2\)\(3\)的子串。然后对于给定的字符串,对于当前位置\(i\),判断包含以位置\(i\)结尾的长度\(2\)\(3\)的子串是否在\(n\)个字符串中出现过,若出现过则记录前驱。最后根据前驱输出答案。

/* Author: cyanine_farewell
 * Time: 2022-01-20 01:16:59
**/
#include<bits/stdc++.h>
#define pb push_back
#define ll long long
#define fast ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
const int maxn = 1010;
int n,m;
map<string,bool> vis;
map<string,tuple<int,int,int>> pos;
bool dp[maxn];
int pr[maxn];
int main()
{
    fast;
    int T;
    cin>>T;
    while(T--)
    {
        pos.clear();
        vis.clear();
        string s;
        string t;
        cin>>n>>m;
        for(int i=0;i<=m;i++)
        {
            dp[i]=0;
            pr[i]=0;
        }
        dp[0]=1;
        for(int i=1;i<=n;i++)
        {
            cin>>s;
            for(int j=0;j<m;j++)
            {
                t=s[j];
                for(int k=1;k<=2;k++)
                {
                    if(k+j>=m) break;
                    t+=s[j+k];
                    if(!vis[t])
                    {
                        vis[t]=1;
                        pos[t]=make_tuple(j,j+k,i);
                    }
                }
            }
        }
        cin>>s;
        for(int i=0;i<m;i++)
        {
            t=s[i];
            for(int k=1;k<=2;k++)
            {
                if(i-k<0) break;
                t=s[i-k]+t;
                if(vis[t] && dp[i-k])
                {
                    dp[i+1]=1;
                    pr[i+1]=i-k;
                }
                if(dp[i+1]) break;
            }
        }
        if(!dp[m])
        {
            cout<<"-1\n";
            continue;
        }
        vector<tuple<int,int,int>> ans;
        for(int k=m;k>0;)
        {
            int p=pr[k];
            string t=s.substr(p,k-p);
            ans.emplace_back(pos[t]);
            k=p;
        }
        reverse(ans.begin(),ans.end());
        cout<<ans.size()<<'\n';
        for(auto [l,r,i]:ans)
        {
            cout<<l+1<<' '<<r+1<<' '<<i<<'\n';
        }
    }
    return 0;
}

F - Interacdive Problem

题意:交互,给出\(n\),猜测\(x(1\leq x < n)\)的值。交互方式为:给出\(c\)\(x=x+c\),返回\(\lfloor \frac{x}{n} \rfloor\)。交互不超过\(10\)次,\(1\leq n \leq 1000\)

思路:补题的时候感觉题解的思路不太符合自己的常规想法,改了一些小的细节。假定当前余数范围为\([l,r]\),以第一次为例,余数范围为\([1, n-1]\),进行二分余数的操作,取得\(mid\)后给出\(c=n-mid\),如果返回的结果比上一次大,说明余数范围在\([mid,r]\),反之在\([l,mid]\)。同时,由于\(x=x+c\),因此余数范围也发生了变化,需要加上\(c\)并进行取模。可以在对数次数内找到结果,也就是\(10\)次内。

需要注意的是,这个思路在\(l=0,r=1,mid=(l+r)/2\)时会进入死循环,因此将\(mid=(l+r+1)/2\)即可。

最终结果为最后一次返回的结果加上\(l\)(或者\(r\),因为此时\(l==r\))。

/* Author: cyanine_farewell
 * Time: 2022-01-19 19:30:03
**/
#include<bits/stdc++.h>
#define pb push_back
#define ll long long
#define fast ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
const int maxn = 100010;
int n;
int main()
{
    fast;
    cin>>n;
    int l=1,r=n-1;
    int d=0;
    int x;
    int ans;
    while(l<r)
    {
        int mid=(l+r+1)/2;
        cout<<"+ "<<n-mid<<endl;
        cin>>x;
        if(x>d) l=mid;
        else r=mid-1;
        l=(l+n-mid)%n;
        r=(r+n-mid)%n;
        d=x;
    }
    cout<<"! "<<d*n+l<<endl;
    return 0;
}

G - MinOr Tree

题意:给出一张图,问对于或运算的最小生成树的值。

思路:不需要考虑具体留下哪些边。按位从高位进行处理,判断在去掉该位为\(1\)的边后剩余的边能否连通,若能则去掉这些边,不能则保留。连通性利用并查集可以判断。

/* Author: cyanine_farewell
 * Time: 2022-01-19 19:30:03
**/
#include<bits/stdc++.h>
#define pb push_back
#define ll long long
#define fast ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
const int maxn = 200010;
struct Edge{
    int u,v,w;
};
bool vis[maxn];
Edge E[maxn];
int f[maxn];
int n,m;
int Find(int x)
{
    if(f[x]==x) return x;
    return f[x]=Find(f[x]);
}
void Union(int x,int y)
{
    x=Find(x);
    y=Find(y);
    if(x==y) return;
    f[x]=y;
    return;
}
bool check(int x)
{
    for(int i=1;i<=n;i++)
    {
        f[i]=i;
    }
    for(int i=1;i<=m;i++)
    {
        if(vis[i] || E[i].w&(1<<x)) continue;
        int u=E[i].u,v=E[i].v;
        Union(u,v);
        Union(v,u);
    }
    for(int i=1;i<=n;i++)
    {
        if(Find(1)!=Find(i)) return 0;
    }
    return 1;
}
int main()
{
    fast;
    int T;
    cin>>T;
    while(T--)
    {
        cin>>n>>m;
        for(int i=1;i<=m;i++)
        {
            vis[i]=0;
            cin>>E[i].u>>E[i].v>>E[i].w;
        }
        int res=0;
        for(int i=30;i>=0;i--)
        {
            if(check(i))
            {
                for(int j=1;j<=m;j++)
                {
                    if(E[j].w&(1<<i)) vis[j]=1;
                }
            }
            else res+=(1<<i);
        }
        cout<<res<<'\n';
    }
    return 0;
}
posted @ 2022-01-21 14:21  cyanine_告别  阅读(80)  评论(0编辑  收藏  举报