Codeforces Round #606 (Div. 2, based on Technocup 2020 Elimination Round 4) A B C D E F

>>>这场比赛的链接


Problem A

题意:t组数据,每组给出一个n,询问1~n中各个位上都相同的数的个数,比如1~10有1 2 3 4 5 6 7 8 9这九个数满足,答案是9。

思路:1~9,10~99,100~999,...各自都有9位,所以获取一下最高位的位数,最高位特殊处理一下+(最高位的位数-1)*9就是答案了。

#include<bits/stdc++.h>
    
using namespace std;
    
#define ll long long
#define for1(i,a,b) for (int i=(a);i<=(b);i++)
#define for0(i,a,b) for (int i=(a);i<(b);i++)
#define rof1(i,a,b) for (int i=(a);i>=(b);i--)
#define rof0(i,a,b) for (int i=(a);i>(b);i--)
#define pb push_back
#define fi first
#define se second
#define duozu int __T;scanf("%d",&__T);for1(ica,1,__T)
void _W(const int &x) { printf("%d", x); }
void _W(const ll &x) { printf("%I64d", x); }
void _W(const double &x) { printf("%.16f", x); }
void _W(const char &x) { putchar(x); }
void _W(const char *x) { printf("%s", x); }
void W(){}
template<class T, class... U> void W(const T &head, const U &... tail) { _W(head); putchar(sizeof...(tail) ? ' ' : '\n'); W(tail...); }
void _R(const int& x){ scanf("%d", &x); }
void _R(const ll& x){ scanf("%I64d", &x); }
void _R(const char* x){ scanf("%s", x); }
void R(){}
template<class T,class... U> void R(const T& head,const U& ...tail){_R(head);R(tail...);}
const ll mod = 1e9+7;
ll ft1(ll a,ll b){ll ans = 1;while (b){if (b&1) ans *= a;b>>=1,a*=a;}return ans;}
ll ft2(ll a,ll b){ll ans = 1;while (b){if (b&1) ans = ans*a%mod;b>>=1,a=a*a%mod;}return ans;}
    
const int N = 1e5+5;
    
    
    
int main()
{
    duozu{
        int n;
        R(n);
        
        int num=0,t = n;
        while (t) num++,t/=10;
    
        int mod = 1;
        for1(i,1,num-1) mod*=10;
    
        int high = n/mod;
    
        int bord = 0;
    
        for1(i,1,num) bord = bord*10+high;
    
        int ans = 9*(num-1);
    
        //W("n=",n,"bord=",bord,"num=",num,"high=",high);
    
        if (n>=bord) ans += high;
        else ans += high-1;
    
        W(ans);
    }
    return 0;
}

官方题解的过程更清晰,通过找出每个数和n进行比较

cin >> n;
int b = 0, ans = 0;
for (int len = 1; len <= 9; len++) {
    b = b * 10 + 1;            
    for (int m = 1; m <= 9; m++)
        if (b * m <= n)
            ans++;
}
cout << ans << endl;

Problem B

题意:t组数据,每组n个数,每次操作可以选取一个偶数x,会将这n个数中所有等于x的都/2,询问最少多少次可以将数列中的数全部变为奇数。

思路:首先一定是先从大的偶数开始/2,这样更有利于聚集更多相同偶数一起/2,因此我们对n个数降序排序,然后我们记录当前的数变为奇数需要多少次,然后标记途中的中间数,这些数如果等会与之后进行操作的n个数中的数相等,那么那个数就不需要进行操作就可以变成奇数了——因为之前已经处理过这个数变成奇数的过程。

#include<bits/stdc++.h>
    
using namespace std;
    
#define ll long long
#define for1(i,a,b) for (int i=(a);i<=(b);i++)
#define for0(i,a,b) for (int i=(a);i<(b);i++)
#define rof1(i,a,b) for (int i=(a);i>=(b);i--)
#define rof0(i,a,b) for (int i=(a);i>(b);i--)
#define pb push_back
#define fi first
#define se second
#define duozu int __T;scanf("%d",&__T);for1(ica,1,__T)
void _W(const int &x) { printf("%d", x); }
void _W(const ll &x) { printf("%I64d", x); }
void _W(const double &x) { printf("%.16f", x); }
void _W(const char &x) { putchar(x); }
void _W(const char *x) { printf("%s", x); }
void W(){}
template<class T, class... U> void W(const T &head, const U &... tail) { _W(head); putchar(sizeof...(tail) ? ' ' : '\n'); W(tail...); }
void _R(const int& x){ scanf("%d", &x); }
void _R(const ll& x){ scanf("%I64d", &x); }
void _R(const char* x){ scanf("%s", x); }
void R(){}
template<class T,class... U> void R(const T& head,const U& ...tail){_R(head);R(tail...);}
const ll mod = 1e9+7;
ll ft1(ll a,ll b){ll ans = 1;while (b){if (b&1) ans *= a;b>>=1,a*=a;}return ans;}
ll ft2(ll a,ll b){ll ans = 1;while (b){if (b&1) ans = ans*a%mod;b>>=1,a=a*a%mod;}return ans;}
    
const int N = 2e5+5;
    
map<ll,int>mp;
    
int a[N];
    
bool cmp(int a,int b){return a>b;}
    
int main()
{
    duozu{
        mp.clear();
        int n;
        R(n);
        for1(i,1,n) R(a[i]);
    
        sort(a+1,a+1+n,cmp);
    
        //for1(i,1,n) W(a[i]);
    
        int ans = 0;
    
        for1(i,1,n){
            if (a[i]&1) continue;
            if (mp.count(a[i])==0){
                mp[a[i]] = 1;
                while (a[i]%2==0){
                    a[i]/=2;
                    mp[a[i]] = 1;
                    ans++;
                }
            }
        }
    
        W(ans);
    
    }
    return 0;
}

Problem C

题意:t组数据,每组给出一个串由twone五个字符任意数量组成,询问你最少删除几个字符以及这几个字符的位置使得串中不出现one和two(这几个字符同时删除)

思路:我们删掉的字母必须是作用最大的——即删除这个字母可以做到删掉其他好几个字母的事情。我用了看毛片KMP算法分别去one和two。

不论one还是two,我们都优先删掉o字母,因为o可以避免两种串的产生。

除非遇到...oone以及twoo...的情况,此时删掉n和w是最好的选择。

(代码中我把删掉的操作转化为把这个字符赋值为‘1’,这样仿佛会存在的问题是,我匹配完one,然后原字符串并没有实际的删减而导致漏匹配一些two的情况——这种情况是不存在的因为我们处理完one之后的串中有o1e,..oo1e两种情况,很显然没有截断任何有价值的情况)

#include<bits/stdc++.h>
    
using namespace std;
    
#define ll long long
#define for1(i,a,b) for (int i=(a);i<=(b);i++)
#define for0(i,a,b) for (int i=(a);i<(b);i++)
#define rof1(i,a,b) for (int i=(a);i>=(b);i--)
#define rof0(i,a,b) for (int i=(a);i>(b);i--)
#define pb push_back
#define fi first
#define se second
#define duozu int __T;scanf("%d",&__T);for1(ica,1,__T)
void _W(const int &x) { printf("%d", x); }
void _W(const ll &x) { printf("%I64d", x); }
void _W(const double &x) { printf("%.16f", x); }
void _W(const char &x) { putchar(x); }
void _W(const char *x) { printf("%s", x); }
void W(){}
template<class T, class... U> void W(const T &head, const U &... tail) { _W(head); putchar(sizeof...(tail) ? ' ' : '\n'); W(tail...); }
void _R(const int& x){ scanf("%d", &x); }
void _R(const ll& x){ scanf("%I64d", &x); }
void _R(const char* x){ scanf("%s", x); }
void R(){}
template<class T,class... U> void R(const T& head,const U& ...tail){_R(head);R(tail...);}
const ll mod = 1e9+7;
ll ft1(ll a,ll b){ll ans = 1;while (b){if (b&1) ans *= a;b>>=1,a*=a;}return ans;}
ll ft2(ll a,ll b){ll ans = 1;while (b){if (b&1) ans = ans*a%mod;b>>=1,a=a*a%mod;}return ans;}
    
const int N = 150000+5;
    
char s[N];
int fail[N];
char ss[2][10]={"one","two"};
int ans;
int pos[N],idx;
    
void kmp(char* s,char* ss){
    int len = strlen(ss);
    fail[0] = -1;
    for (int i=0,j=-1;i<len;){
        if (j==-1||ss[i]==ss[j]){
            i++;j++;
            fail[i] = j;
        }
        else j = fail[j];
    }
    
    int llen = strlen(s);
    for (int i=0,j=0;i<llen;){
        if (j==-1||s[i]==ss[j]){
            i++;j++;
        }
        else j = fail[j];
        if (j==len){
            if (s[i-1]=='o'){///two
                if (s[i]=='o') s[i-2]='1',pos[ans++] = i-2;
                else s[i-1]='1',pos[ans++] = i-1; 
            }
            else {///one
                if (i-4>=0 && s[i-4]!='o') s[i-3] = '1',pos[ans++]=i-3; 
                else s[i-2]='1',pos[ans++] = i-2;
            }
        }
    }
    
}
    
int main()
{
    duozu{
        R(s);
        ans = idx = 0;
        kmp(s,ss[0]);
        kmp(s,ss[1]);
        sort(pos,pos+ans);
        W(ans);
        for0(i,0,ans){
            if (i!=0) printf(" ");
            printf("%d",pos[i]+1);
        }puts("");
    }
    return 0;
}

Problem D

题意:t组数据,每组n个01串。

1.串a串b可连接成ab的条件是a的末尾 = b的开头

2.我们可以进行的操作有翻转一个串

3.询问最少多少次可以把所有串连接并给出这些串的位置

4.最终不允许有重复串(当然保证初始状态没有重复不然做个P)

思路:用开头结尾分类成四种:01,10,11,00

我们发现00,11根本没啥用,只要有01或者10,存在11,00必定可以连上去——除非11,00同时存在且没有01或者10

排除上面那种状况后,我们假设01有a个,10有b个

怎样才能连成成一条取决于a,b的数量

我们发现|a-b| = 0 或者 1时可以连接起来,直接抽象来说——一种黑砖一种白砖间隔着拼起来那么可以ababa,也可以abab。

所以我们将多的那一种瓷砖转化|a-b|/2块。

麻烦的地方在于不能重复...

我的思路是黑白砖头各自开一个map<string,int>,mp[串] = 位置。

然后其中一个存反串,这样就能直接判断这个串的反串是否在另一个串中出现。

#include<bits/stdc++.h>
    
using namespace std;
    
#define ll long long
#define for1(i,a,b) for (int i=(a);i<=(b);i++)
#define for0(i,a,b) for (int i=(a);i<(b);i++)
#define rof1(i,a,b) for (int i=(a);i>=(b);i--)
#define rof0(i,a,b) for (int i=(a);i>(b);i--)
#define pb push_back
#define fi first
#define se second
#define duozu int __T;scanf("%d",&__T);for1(ica,1,__T)
void _W(const int &x) { printf("%d", x); }
void _W(const ll &x) { printf("%I64d", x); }
void _W(const double &x) { printf("%.16f", x); }
void _W(const char &x) { putchar(x); }
void _W(const char *x) { printf("%s", x); }
void W(){}
template<class T, class... U> void W(const T &head, const U &... tail) { _W(head); putchar(sizeof...(tail) ? ' ' : '\n'); W(tail...); }
void _R(const int& x){ scanf("%d", &x); }
void _R(const ll& x){ scanf("%I64d", &x); }
void _R(const char* x){ scanf("%s", x); }
void R(){}
template<class T,class... U> void R(const T& head,const U& ...tail){_R(head);R(tail...);}
const ll mod = 1e9+7;
ll ft1(ll a,ll b){ll ans = 1;while (b){if (b&1) ans *= a;b>>=1,a*=a;}return ans;}
ll ft2(ll a,ll b){ll ans = 1;while (b){if (b&1) ans = ans*a%mod;b>>=1,a=a*a%mod;}return ans;}
    
const int N = 4e6+5;
    
char s[N],ss[N];
int p01[N],p10[N];
int ans[N];
    
unordered_map<string,int>mp01,mp10;
    
int main()
{
    duozu{
        mp01.clear();
        mp10.clear();
        int n;
        R(n);
        int a01=0,a10=0,a11=0,a00=0;
        for1(i,1,n){
            R(s);
            int len = strlen(s);
            if (s[0]=='0' && s[len-1]=='0')  a00++;
            if (s[0]=='0' && s[len-1]=='1')  {p01[a01++] = i;mp01.count(s)==0?mp01[s] = i:mp01[s]++;}
            if (s[0]=='1' && s[len-1]=='0')  {p10[a10++] = i;for0(i,0,len)ss[i] = s[len-i-1];ss[len] = '\0';mp10.count(ss)==0?mp10[ss] = i:mp10[ss]++; }
            if (s[0]=='1' && s[len-1]=='1')  a11++;
        }
        if (a01==0 && a10==0 && a00!=0 && a11!=0) puts("-1");
        else {
            int num = abs(a01-a10)/2;
            int idx = 0;
            
            if (a01>a10){//找到num个01中翻转后不存在与10中的串(mp10.count(s)==0)
                for (auto it = mp01.begin();it!=mp01.end();it++){
                    //cout << it->fi << endl;
                    if (mp10.count(it->fi)==0){
                        ans[idx++] = it->se;
                    }
                }
            }
            else if (a01<a10){//找到num个01中翻转后不存在与10中的串(mp10.count(s)==0)
                for (auto it = mp10.begin();it!=mp10.end();it++){
                    if (mp01.count(it->fi)==0){
                        ans[idx++] = it->se;
                    }
                }
            }
            if (idx>=num){
                W(num);
                for (int i=0;i<num;i++){
                    if (i!=0) putchar(' ');
                    printf("%d",ans[i]);    
                }
                puts("");
            }
            else puts("-1");
        }
    }
    return 0;
}

Problem E

题意:一个图中有ab两点,询问必须经过ab才能到达的点的对数。

思路:染色两边,将只能过a的点染为1,只能过b的点染为2,同时能过的染为3,答案等于1的数量*2的数量

#include<bits/stdc++.h>
    
using namespace std;
    
#define ll long long
#define for1(i,a,b) for (int i=(a);i<=(b);i++)
#define for0(i,a,b) for (int i=(a);i<(b);i++)
#define rof1(i,a,b) for (int i=(a);i>=(b);i--)
#define rof0(i,a,b) for (int i=(a);i>(b);i--)
#define pb push_back
#define fi first
#define se second
#define duozu int __T;scanf("%d",&__T);for1(ica,1,__T)
void _W(const int &x) { printf("%d", x); }
void _W(const ll &x) { printf("%I64d", x); }
void _W(const double &x) { printf("%.16f", x); }
void _W(const char &x) { putchar(x); }
void _W(const char *x) { printf("%s", x); }
void W(){}
template<class T, class... U> void W(const T &head, const U &... tail) { _W(head); putchar(sizeof...(tail) ? ' ' : '\n'); W(tail...); }
void _R(const int& x){ scanf("%d", &x); }
void _R(const ll& x){ scanf("%I64d", &x); }
void _R(const char* x){ scanf("%s", x); }
void R(){}
template<class T,class... U> void R(const T& head,const U& ...tail){_R(head);R(tail...);}
const ll mod = 1e9+7;
ll ft1(ll a,ll b){ll ans = 1;while (b){if (b&1) ans *= a;b>>=1,a*=a;}return ans;}
ll ft2(ll a,ll b){ll ans = 1;while (b){if (b&1) ans = ans*a%mod;b>>=1,a=a*a%mod;}return ans;}
    
const int N = 2e5+5;
const int M = 5e5+5;
    
struct E{
    int to,last;
}edge[M<<1];
int id,head[N];//双向建边记得X2
void add(int u,int v){edge[id].to = v;edge[id].last = head[u];head[u] = id++;}
int col[N];
bool first;
    
void dfs(int now,int cc,int out){
    if (first) first = false;
    else {
        if (col[now]>=out) return ;
        col[now] += cc;
    }
    for (int i=head[now];i!=0;i=edge[i].last){
        int v = edge[i].to;
        dfs(v,cc,out);
    }
}
    
int main()
{
    int n,m,a,b;
    int c1,c2;
    duozu{
        R(n,m,a,b);
        id = 1;
        for1(i,1,n) head[i] = 0,col[i] = 0;
        c1 = c2 = 0;
        int u,v;
        for1(i,1,m) R(u,v),add(u,v),add(v,u);
        col[a] = col[b] = 4;
        first = true,dfs(a,1,1);
        first = true,dfs(b,2,2);
        for1(i,1,n){ 
            //W(i,"=",col[i]);
            if (col[i]==1) c1++;
            else if (col[i]==2) c2++;
        }
        W(1LL*c1*c2);
    }
    return 0;
}

Problem F

题意:给出n个数,进可能多的利用这些数,拼出一个矩形,要求每一行每一列元素不能重复

思路:需要先发现一些规律,我们假定矩形为r*c:c行,每行r个(我当时定义反了所以现在也这么说无所谓啦)

并且规定r<c,那么我们会发现一个数最多存在r次,我们发现只要按照(i,j),(i+1,j+1)...的顺序排下去(超过了边界就取余)一定可以符合要求,有些数小于r个,那么一定可以被利用,你想:我333都不会出问题,现在我233不就更不可能出问题吗。

我们把数据处理一下后,维护一个升序的记录每种数数量的数组,再维护一下前缀和

因此,第一维O(N)枚举r,然后二分查找一下第一个大于等于r的位置p,可以利用的数的数量为前p-1个的前缀个+剩余的每个利用r个,这个数量/r就是c的大小。

枚举完之后我们获取最大情况下的r和c然后接下去利用上面所说的方法去铺就行了。

#include<bits/stdc++.h>
    
using namespace std;
    
#define ll long long
#define for1(i,a,b) for (int i=(a);i<=(b);i++)
#define for0(i,a,b) for (int i=(a);i<(b);i++)
#define rof1(i,a,b) for (int i=(a);i>=(b);i--)
#define rof0(i,a,b) for (int i=(a);i>(b);i--)
#define pb push_back
#define fi first
#define se second
#define duozu int __T;scanf("%d",&__T);for1(ica,1,__T)
void _W(const int &x) { printf("%d", x); }
void _W(const ll &x) { printf("%I64d", x); }
void _W(const double &x) { printf("%.16f", x); }
void _W(const char &x) { putchar(x); }
void _W(const char *x) { printf("%s", x); }
void W(){}
template<class T, class... U> void W(const T &head, const U &... tail) { _W(head); putchar(sizeof...(tail) ? ' ' : '\n'); W(tail...); }
void _R(const int& x){ scanf("%d", &x); }
void _R(const ll& x){ scanf("%I64d", &x); }
void _R(const char* x){ scanf("%s", x); }
void R(){}
template<class T,class... U> void R(const T& head,const U& ...tail){_R(head);R(tail...);}
const ll mod = 1e9+7;
ll ft1(ll a,ll b){ll ans = 1;while (b){if (b&1) ans *= a;b>>=1,a*=a;}return ans;}
ll ft2(ll a,ll b){ll ans = 1;while (b){if (b&1) ans = ans*a%mod;b>>=1,a=a*a%mod;}return ans;}
    
const int N = 4e5+5;
    
int a[N];
ll sum[N];
struct node{
    int num;
    int cnt;
    bool operator < (const node a){
        return cnt < a.cnt;
    }
}b[N];
int mp[N];
    
int main()
{
    int n,x;
    while (~scanf("%d",&n)){
        for1(i,1,n) R(a[i]);
        sort(a+1,a+1+n);
        int now = 1,tot = 0;
        while (now<=n){
            int cnt = 1;
            while (now<n && a[now+1]==a[now]) now++,cnt++;
            b[tot].num = a[now];
            b[tot++].cnt = cnt;
            now++;
        }
        sort(b,b+tot);
        for0(i,0,tot) a[i] = b[i].cnt;
        
        sum[0] = a[0];
        for0(i,1,tot) sum[i] = sum[i-1]+a[i];
        
        int c=0,r=0;
    
        //W("num =",tot);
    
        for (int cc = 1;cc*cc<=n;cc++){//每行的个数
            int more_than_cc = lower_bound(a,a+tot,cc) - a;
            int mrc = more_than_cc;
            //W("mrc =",mrc);
            int rr =  ( ((mrc>0)?sum[mrc-1]:0) + ((mrc==tot)?0:1LL*cc*(tot-mrc)) )/cc;
            if (rr>=cc && 1LL*cc*rr>1LL*r*c){
                c = cc;
                r = rr;
            } 
        }
    
        W(c*r);
        W(r,c);
    
        int mrc = lower_bound(a,a+tot,c) - a;
        int idx = 0;
        for0(i,mrc,tot){
            int now = c;
            while (now--) a[idx++] = b[i].num;
        }  
        for0(i,0,mrc){
            while (b[i].cnt--) a[idx++] = b[i].num;
        }
        //for0(i,0,idx) W(a[i]);
    
        idx = 0;
    
        for (int i=1;i<=r;i++){
            int ii = i,j = 1;
            int cishu = c;
            while (cishu--){
                mp[(ii-1)*c+j] = a[idx++];
                ii++;if (ii==r+1) ii = 1;
                j++; if ( j==c+1)   j = 1;
            }  
        }
    
        for1(i,1,r){
            for1(j,1,c){
                if (j!=1) printf(" ");
                printf("%d",mp[(i-1)*c+j]);
            }
    
            puts("");
        }
    
    }
    return 0;
}

加油

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值