Codeforces Round #582 (Div. 3) codeforces 1213

题目链接

ABCD1水题。

哎,两个小时就除了这四个题,出题太慢了......

D2:

题意:给你n个数,每次可以选任意一个数,让它除以等于2(向下取整),可以操作任意多次,问最少操作多少次能使这n个数中有k个相等的数。

思路:用vector存一下就行了。做题的时候光想dp了,实际上不是dp,就是个思维题。

#include<bits/stdc++.h>
#define mem(a,b) memset((a),b,sizeof(a))
#define de cout<<endl<<endl<<endl
typedef long long ll;
const int inf=0x3f3f3f3f;
const int maxn=2e5+10;
using namespace std;
int n,k,a[maxn];
vector<int> v[maxn];
int main()
{

    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    if(k==1)
        {printf("0\n");return 0;}
    for(int i=1;i<=n;i++)
    {
        int p=a[i],cnt=0;
        while(p>=0)
        {
            v[p].push_back(cnt);
            if(p==0)
                break;
            p/=2;
            cnt++;
        }
    }
    int ans=inf,anss;
    for(int i=0;i<maxn;i++)
    {
        if(v[i].size()>=k)
        {
            sort(v[i].begin(),v[i].end());
            anss=0;
            for(int j=0;j<k;j++)
                anss+=v[i][j];
            ans=min(ans,anss);
        }
    }
    printf("%d\n",ans);
    return 0;
}

E:

题意:给你一个整数n,再给你两个长度均为2的字符串(均有abc三个字符构成),问:能否找到一个长度为3n的字符串满足abc三个字符的数目均为n,并且上面两个字符串都不是这个字符串的子串,输出这个字符串,如果不存在这样的字符串输出NO。

思路:实际上这题答案一定存在,做的时候我也猜到了,就是不知道答案那个字符串是啥。

就是这个答案可以由简单的字符变换出来,比如:abc可以变换成aabbcc,aaabbbccc,或者abcabc,abcabcabc,就这两个情况,对abc搞个全排列,然后把这两种变换都是一下,一定能找到结果。

具体=可以看这篇博客:https://www.cnblogs.com/wawcac-blog/p/11464774.html

#include<bits/stdc++.h>
#define mem(a,b) memset((a),b,sizeof(a))
#define de cout<<endl<<endl<<endl
typedef long long ll;
const int inf=0x3f3f3f3f;
const int maxn=3e5+10;
using namespace std;
int n;
char s1[3],s2[3];
int a[3],b[3];
int ans[maxn];
int cnt[4],p[4];
bool ok()
{
    for(int i=1;i<=min(3*n-1,36);i++)
    {
        if(ans[i]==a[1]&&ans[i+1]==a[2])    return false;
        if(ans[i]==b[1]&&ans[i+1]==b[2])    return false;
    }
    return true;
}
bool okk()
{
    if(n<=12)
    {
        for(int i=1;i<=3*n-1;i++)
        {
            if(ans[i]==a[1]&&ans[i+1]==a[2])    return false;
            if(ans[i]==b[1]&&ans[i+1]==b[2])    return false;
        }
        return true;
    }
    if(a[1]==a[2])  return false;
    if(b[1]==b[2])  return false;
    if(p[1]==a[1]&&p[2]==a[2])  return false;
    if(p[2]==a[1]&&p[3]==a[2])  return false;
    if(p[1]==b[1]&&p[2]==b[2])  return false;
    if(p[2]==b[1]&&p[3]==b[2])  return false;
    return true;
}
void print()
{
    puts("YES");
    for(int i=1;i<=3*n;i++)
    {
        if(ans[i]==1)   printf("a");
        if(ans[i]==2)   printf("b");
        if(ans[i]==3)   printf("c");
    }
    puts("");return ;
}
int main()
{
    scanf("%d",&n);
    scanf("%s%s",s1+1,s2+1);
    a[1]=s1[1]-'a'+1;
    a[2]=s1[2]-'a'+1;
    b[1]=s2[1]-'a'+1;
    b[2]=s2[2]-'a'+1;
    p[1]=1;p[2]=2;p[3]=3;
    do
    {
        for(int i=1;i<=n;i++)
            ans[i]=p[1];
        for(int i=1;i<=n;i++)
            ans[i+n]=p[2];
        for(int i=1;i<=n;i++)
            ans[i+2*n]=p[3];
        if(okk())
            {print();return 0;}
        for(int i=0;i<=n-1;i++)
            ans[1+3*i]=p[1],ans[2+3*i]=p[2],ans[3+3*i]=p[3];
        if(ok())
            {print();return 0;}
    }while(next_permutation(p+1,p+3+1));
    puts("NO");
    return 0;
}

G:

题意:一个有向图,每条边都有一个权值,有m个询问,每个询问找出有多少对点使得这些点对之间的最大边权不大于q。

思路:

把询问离线,对于每个询问,我们把比他的要求小的边全部加进树中。

那么答案就是所有连通块的贡献,每个连通块的贡献是sz*(sz-1)/2

那么我们按询问的要求值从小到大排序,然后每次尝试加边,并用并查集维护连通块大小

参考博客:https://blog.csdn.net/liufengwei1/article/details/100168298

#include<bits/stdc++.h>
#define mem(a,b) memset((a),b,sizeof(a))
#define de cout<<endl<<endl<<endl
typedef long long ll;
const int inf=0x3f3f3f3f;
const int maxn=2e5+10;
using namespace std;
struct node{
    int u,v,edge;
}a[maxn];
struct nodee{
    int q,id;
    ll anss;
}ans[maxn];
int n,m,fa[maxn];
ll Size[maxn];
bool cmp1(node a1,node a2)  {return a1.edge<a2.edge;}
bool cmp2(nodee a1,nodee a2)  {return a1.q<a2.q;}
bool cmp3(nodee a1,nodee a2)  {return a1.id<a2.id;}
int findfa(int x)   {return fa[x]==x?x:fa[x]=findfa(fa[x]);}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n-1;i++)
        scanf("%d%d%d",&a[i].u,&a[i].v,&a[i].edge);
    sort(a+1,a+n,cmp1);
    for(int i=1;i<=m;i++)
        scanf("%d",&ans[i].q),ans[i].id=i;
    sort(ans+1,ans+m+1,cmp2);
    for(int i=1;i<=n;i++)
        fa[i]=i,Size[i]=1;
    int p=1;
    ans[1].anss=0;
    for(int i=1;i<=m;i++)
    {
        ll tmp=ans[i-1].anss;
        ans[i].anss=tmp;
        while(a[p].edge<=ans[i].q&&p<=n-1)
        {
            int uu=a[p].u,vv=a[p].v;
            int fau=findfa(uu),fav=findfa(vv);
            if(fau!=fav)
            {
                fa[fau]=fav;
                tmp-=Size[fau]*(Size[fau]-1)/2;
                tmp-=Size[fav]*(Size[fav]-1)/2;
                Size[fav]+=Size[fau],Size[fau]=0;
                tmp+=Size[fav]*(Size[fav]-1)/2;
                ans[i].anss=tmp;
            }
            p++;
        }
    }
    sort(ans+1,ans+m+1,cmp3);
    printf("%I64d",ans[1].anss);
    for(int i=2;i<=m;i++)
        printf(" %I64d",ans[i].anss);
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值