Codeforces Round 606 Div2

A

题意

给一个n,求范围[1,n]内所有数中,所有数位都相等的数的个数,如111,22,3

思路

求出n的位数d,则数位长度为[1,n-1]的数各有9个,再考虑数位长度为n的数,从高位向低位推,设最高位数字为k,如果当前位等于k,则继续往下推。否则,若大于k,则最可取k个,若小于,则只能取k-1个。最后计算一下答案。

代码

#include<bits/stdc++.h>

using namespace std;

int d[15];

int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        int n,len=0;
        scanf("%d",&n);
        while(n)
        {
            d[len++]=n%10;
            n/=10;
        }
        int res=0,flag=0;
        for(int i=len-2;i>=0;i--)
        {
            if(d[i]==d[len-1])
                continue;
            if(d[i]<d[len-1])
                flag=1;
            break;
        }
        res=d[len-1]-flag+9*(len-1);
        printf("%d\n",res);
    }
}

B

题意

给定一个序列,有奇有偶。偶数k可以通过一次操作变为k/2。所有相同的k可以用一次操作同时变为k/2。求使得整个序列全部为奇数所需要的最少操作次数。

思路

k/2等价于k>>1,所以很容易可以想到对于同一个奇数所扩展出来的偶数,只需要取最大所需操作即可,如12,6,都由3扩展而来,对12进行一次操作会得到两个6,再一次操作即可完成目标。所以方法就很明确了,扫一遍序列,记录一下当前数由那个奇数扩展而来,然后用map记录一下最大值,最后加起来就好了。

代码

#include<bits/stdc++.h>

using namespace std;
const int MAX=2e5+5;

int odd[MAX];
map<int,int>mp;

int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        int n,x,len=0,res=0;
        mp.clear();
        scanf("%d",&n);
        for(int i=0;i<n;i++)
        {
            scanf("%d",&x);
            if(x&1)continue;
            int cur=0;
            while((x&1)==0)
                x>>=1,cur++;
            if(!mp[x])
                odd[len++]=x;
            mp[x]=max(mp[x],cur);
        }
        for(int i=0;i<len;i++)
            res+=mp[odd[i]];
        printf("%d\n",res);
    }
}

C

题意

给一个字符串,每次操作可以去掉一个字符,求最少操作数使得整个字符串不包含任何onetwo子串。

思路

很显然,除了twone这种情况可以一次操作去掉两个以外,对于每一个onetwo子串都得操作一次,所以先扫一遍,去除twone这种子串的o,再扫一遍去除单独的onetwo,对于单独的这种应该删除中间的字符,因为删除两边的可以会接上,如oonee这种。

代码

#include<bits/stdc++.h>

using namespace std;
const int MAX=2e5+5;

char s[MAX];
int res[MAX];

int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        scanf("%s",s);
        int cnt=0,len=strlen(s);
        for(int i=2;i<len-2;i++)
            if(s[i]=='o')
                if(s[i-1]=='w'&&s[i-2]=='t'&&s[i+1]=='n'&&s[i+2]=='e')
                    s[i]='k',res[cnt++]=i;
        for(int i=1;i<len-1;i++)
        {
            if(s[i]=='w'&&s[i-1]=='t'&&s[i+1]=='o')
                res[cnt++]=i;
            if(s[i]=='n'&&s[i-1]=='o'&&s[i+1]=='e')
                res[cnt++]=i;
        }
        printf("%d\n",cnt);
        for(int i=0;i<cnt;i++)
            printf("%d ",res[i]+1);
        printf("\n");
    }
}

D

题意

给一堆字符串,仅由01组成,每个字符串不重复,然后玩玩接龙,1结尾的接1开头的,以此类推。但是这些字符串可能无法完全拼接在一起。所以给一种操作,每次操作可以翻转某一个字符串,但是必须保证所有字符串不重复。求是否可以使得所有字符串拼接在一起,可以则输出操作次数,否则输出-1

思路

很显然字符串有4种,根据开头结尾记为01,10,00,110011型可以忽略,因为可以不影响结果地接到0110型上。所以只考虑0110型。当且仅当两者数量差距小于等于1时可以完全拼接,所以记录一下数量,然后多出来的一半的翻转分配过去就好了。再用哈希记录一下翻转后的字符串是否出现过,若出现过,则不可翻转,往下推。再特判一下两者数量全为0的情况就可以了。3s时间挺充裕的,所以直接用STL实现。

代码

#include<bits/stdc++.h>

using namespace std;
const int MAX=4e6+6;

vector<pair<int,string> >v01,v10;
unordered_map<string,bool>mp01,mp10;
vector<int>out;

void init()
{
    out.clear();
    v01.clear();
    v10.clear();
    mp01.clear();
    mp10.clear();
}

int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        int n,a=0,b=0,c=0,d=0;
        string s;
        init();
        scanf("%d",&n);
        for(int i=1; i<=n; i++)
        {
            cin>>s;
            int len=s.size();
            if(s[0]==s[len-1])
            {
                if(s[0]=='0')
                    c++;
                else
                    d++;
                continue;
            }
            if(s[0]=='0')
                a++,v01.push_back(make_pair(i,s)),mp01[s]=1;
            if(s[0]=='1')
                b++,v10.push_back(make_pair(i,s)),mp10[s]=1;
        }
        if(a==b)
        {
            if(a!=0)
                printf("0\n\n");
            else
            {
                if(c==0||d==0)
                    printf("0\n\n");
                else
                    printf("-1\n\n");
            }
            continue;
        }
        int ch=abs(a-b)/2,res=abs(a-b)/2;
        if(a<b)
            for(int i=0; i<v10.size(); i++)
            {
                if(ch==0)break;
                s=v10[i].second;
                reverse(s.begin(),s.end());
                if(mp01[s])continue;
                out.push_back(v10[i].first);
                ch--;
            }
        else
            for(int i=0; i<v01.size(); i++)
            {
                if(ch==0)break;
                s=v01[i].second;
                reverse(s.begin(),s.end());
                if(mp10[s])continue;
                out.push_back(v01[i].first);
                ch--;
            }
        if(ch!=0)
        {
            printf("%-1\n\n");
            continue;
        }
        printf("%d\n",res);
        for(int i=0; i<out.size(); i++)
            printf("%d ",out[i]);
        printf("\n");
    }
}

E

题意

给一个无向图,再给出两节点a,b,求有多少点对x,y满足从xy必定经过这两个节点。

思路

理想情况是a,b直接相连,他们的连边为此无向图的割边。此时答案即为a,b各自连接的节点个数之积。可以在此情况上做一下拓展。得到如下模型。

根据此模型我们可以看出,只需要计算a不经过b能到达的节点数,b不经过a能到的的节点数,再排除掉共同可达的节点数就可以变为理想模型。所以用dfs计数即可。注意一下极限情况乘积可能会溢出。

代码

#include<bits/stdc++.h>

using namespace std;
const int MAX=2e5+5;
typedef long long ll;

vector<int>G[MAX];
int vis[MAX],common;

ll dfs(int x,int t,int k)
{
    if(x==t)return 0;
    if(vis[x]&&vis[x]!=k)
        common++;
    vis[x]=k;
    ll ans=1;
    for(int i=0; i<G[x].size(); i++)
        if(vis[G[x][i]]!=k)
            ans+=dfs(G[x][i],t,k);
    return ans;
}

int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        int n,m,a,b;
        scanf("%d%d%d%d",&n,&m,&a,&b);
        for(int i=1; i<=n; i++)
            G[i].clear(),vis[i]=0;
        common=0;
        while(m--)
        {
            int u,v;
            scanf("%d%d",&u,&v)`;
            G[u].push_back(v);
            G[v].push_back(u);
        }
        ll aa=dfs(a,b,1)-1,bb=dfs(b,a,2)-1;
        printf("%I64d\n",(aa-common)*(bb-common));
    }
}
posted @ 2020-02-20 22:02  cryingrain  阅读(68)  评论(0编辑  收藏  举报