码迷,mamicode.com
首页 > 其他好文 > 详细

Codeforces Round #697 (Div. 3) A-G

时间:2021-02-15 12:39:39      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:pos   struct   The   rand   水平   开始   number   std   als   

A. Required Remainder

题意:

\(t\)组样例,判定一个正整数\(n\)是否存在一个大于\(1\)的奇数因子,\((1≤t≤10^4), (2≤n≤10^{14})\)

思路:

打表,发现只有满足\(2^{x}\)的数字不存在奇数因子。

Code:


int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t;
    cin>>t;
    while(t--){
        ll ans=1;
        cin>>n;
        int fg=0;
        rep(i,1,63){
            ans=ans*2;
            if(ans==n){
                fg=1;
            }
        }
        if(fg)cout<<"NO"<<"\n";
        else cout<<"YES"<<"\n";
    }
    return 0;
}

B. New Year‘s Number

题意:

\(t\)组样例,判定一个正整数\(n\)是否能由若干个\(2021\)\(2020\)累加得到,\((1≤t≤10^4), (2≤n≤10^{6})\)

思路:

暴力预处理,用个Map存一下即可。

Code:

map<ll,ll>mp;
set<ll>se2[100];
ll sz[maxn];
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    //cout.tie(0);
    int t;
    cin>>t;
    rep(i,0,1000){
        rep(j,0,1000){
            mp[i*2020*1ll+j*2021*1ll]=1;
        }
    }
    while(t--){
        cin>>n;
        if(mp[n])cout<<"YES"<<"\n";
        else cout<<"NO"<<"\n";
    }
    return 0;
}

C. Ball in Berland

题意:

\(t\)组样例,给定\(k\)个组合,每个组合有一个男生和一个女生,每个同学都有一个编号,每个男生的编号小于等于\(a\),每个女生的编号小于等于\(b\),问有多少种选法能选出\(2\)个组合,使得\(2\)个组合中,男生的编号不会同时出现,且女生的编号不会同时出现,\((1≤t≤10^4), (2≤n≤10^{5})\)

思路:

直接做,对每个组合往左求贡献。

先按男生的编号排序,然后考虑排序后的序列中,男生的状态为,\(a_1,a_2,a_2...,a_3,a_3...,a_4...a_k,(a_1<a_2<...<a_k)\)。于是将男生编号分组,相同的编号分为一组。

接下来对同一组的男生进行讨论:

枚举组内男生,假设当前组内男生编号为\(a_i\),对应的女生编号为\(b_i\)。显然同组内的男生不会造成贡献,考虑前面所有组,假设前面所有组总人数为\(cnt\)

则当前男生对答案的贡献为\(cnt-mp[b_i]\)(去掉对应的女生编号的个数即可)。

所以现在差一个\(mp\)数组来记录前面所有组中某个女生编号出现的次数,用map简单维护一下即可,同时要注意在当前组内女生编号的更新。

Code:


struct node
{
    ll shang;
    ll xia;
}qq[maxn];
ll sz[maxn];
bool cmp(node a,node b){
    if(a.shang == b.shang){
        return a.xia<b.xia;
    }
    return a.shang<b.shang;
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t;
    cin>>t;
    while(t--){
        ll A,B,k;
        cin>>A>>B>>k;
        rep(i,1,k){
            cin>>a[i];
            qq[i].shang=a[i];
        }
        rep(i,1,k){
            cin>>b[i];
            qq[i].xia=b[i];
        }
        sort(qq+1,qq+1+k,cmp);
        ll cnt=0;//前面的个数
        map<int,int>mp;//记录女生编号出现次数
        ll ans=0;
        rep(i,1,k){
            ll va=qq[i].shang;
            ll va1=qq[i].xia;
            ll pos=i;
            ll pos1=i;
            ll con=0;//当前上层个数
            while(pos<=k&&qq[pos].shang==va){//分组
                con++;
                pos++;
            }
            i=pos-1;
            rep(j,pos1,i){
                ans=ans+(cnt-mp[qq[j].xia]);//记录答案贡献
            }
            rep(j,pos1,i){
                mp[qq[j].xia]++;//下层加进来
            }
            cnt+=con;
        }
        cout<<ans<<"\n";
 
    }
    return 0;
}

D. Cleaning the Phone

题意:

\(t\)组样例,有\(n\) 个物品和一个最低价值 \(m\) ,每一个物品有一个价值 \(a_i\)和 体积 \(b_i\),体积只有可能为 \(1\)\(2\)

你需要选出几个物品,使得它们的价值和大于等于 \(m\)且使体积最小。\((1≤t≤10^4), (1≤n≤2*10^{5}),1≤a_i≤10^{9},1≤b_i≤2\)

思路:

仔细思考,最后的选法无非就是若干个体积为\(1\)和若干个体积为\(2\),于是贪心,将体积为\(1\)\(2\)的价值分开,分别从大到小排序,再分别做前缀和。

然后枚举用了多少个体积为\(1\)的,假设当前枚举到的体积\(1\)的价格为\(sum_{1_i}\),即体积为\(1\)的物品贪心的用了前\(i\)个。

此时需要知道剩下的\(m-sum_{1_i}\)价格,最少需要前\(x\)个体积为\(2\)的价格才能满足。

在体积为\(2\)的前缀和数组中二分即可。时间复杂度\(O(nlogn)\)

Code:

int cmp(ll a,ll b){
    return a>b;
}
ll suma[maxn],sumb[maxn];
int main(){
    int t;
    srand(time(NULL));
    scanf("%d",&t);
    //t=1;
    while(t--){
        cin>>n>>m;
        rep(i,1,n){
            cin>>a[i];
        }
        rep(i,1,n)cin>>b[i];
        ll sum=0;
        ll sumv=0;
        std::vector<ll> v[3];
        rep(i,1,n){
            sum+=a[i];
            sumv+=b[i];
            v[b[i]].pb(a[i]);
        }
        if(sum<m){
            puts("-1");
            continue;
        }
        rep(i,1,2){
            sort(all(v[i]),cmp);
        }
        rep(i,1,2){
            int siz=v[i].size();
            for(int j=1;j<siz;++j){
                v[i][j]+=v[i][j-1];
            }
        }
        int size2=v[2].size();
        int sz=v[1].size();
        ll minn=1e18;
        for(int i=0;i<size2;++i){//体积为1的物品一个都不要
            if(v[2][i]>=m){
                minn=min(minn,2*1ll*(i+1));
                break;
            }
        }
        for(int i=0;i<sz;++i){//枚举体积为1的物品的个数
            if(v[1][i]>=m){
                minn=min(minn,(i+1)*1ll);
            }
            else{
                ll val=m-v[1][i];
                int pos=lower_bound(all(v[2]),val)-v[2].begin();//二分
                if(pos!=size2)minn=min(minn,1ll*(i+1)+(pos+1)*2);
            }
        }
        if(minn!=1e18)
           cout<<minn<<‘\n‘;
        else cout<<"-1"<<‘\n‘;
 
    }
    return 0;
}

E. Cleaning the Phone

题意:

\(t\)组样例,有\(n\)个博主,其中第\(i\)个有\(a_i\)个粉丝。你需要选出 \(k\) 个,使得他们的粉丝总数最多。问有多少种选法,答案对 \(10^9+7\)取模。

\((1≤t≤10^3), (1≤n,k≤10^{3}),1≤a_i≤n\)

思路:

粉丝总数最多,即从大到小取前\(k\)个。
\(a_i\)从大到小排序,并按权值分组。

\((a_1,a_1..a_1) (a_2,a_2..a_2) (a_3,a_3..a_3)... (a_p,a_p..a_p)\)\(a_1<a_2<a_3<...<a_p\)

统计每组数字个数\(a[i]\)。于是可以很明显的发现,假设在第\(Q\)组,若第\(k\)个刚好在此组的结尾,则只有一种情况。若不在此组的末尾,而是在此组的第\(x\)位。

则问题就转换成在该组内,从\(a[Q]\)个物品中选取\(x\)个的方法有多少种,答案为\(C[a[Q]][x]\)

时间复杂度:\(O(n)\)

Code:

bool cmp(ll a,ll b){
    return a>b;
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    //cout.tie(0);
    int t;
    cin>>t;
    rep(i,1,1000)c[i][0]=c[i][i]=1;
 
    rep(i,2,1000){//组合数
        rep(j,1,i){
            c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
        }
    }
   
    while(t--){
        cin>>n>>k;
        rep(i,1,n){
            cin>>q[i];
        }
        sort(q+1,q+1+n,cmp);//从大到小排序
        int con=0;
        rep(i,1,n){
            int pos=i;
            ll va=q[i];
            ++con;
            while(pos<=n&&q[pos]==va){//分组
                a[con]++;//记录每组的个数
                pos++;
            }
            i=pos-1;
        }
            
        int fg=0;
        rep(i,1,con){
            if(k<a[i]){
                fg=1;
                cout<<c[a[i]][k]<<"\n";
                break;
            }
            else{
                k-=a[i];
            }
        }
        if(!fg){//特判
            cout<<1<<"\n";
        }
        rep(i,1,con)a[i]=0;
    }
    return 0;
}

F. Unusual Matrix

题意:

\(t\)组样例,给你两个 \(n*n\)\(01\)矩阵,你可以进行如下两种操作:

垂直xor:选中一列,将这一列的每个元素分别进行xor
水平xor:选中一行,将这一行的每个元素分别进行xor
给定 \(a,b\) 两个矩阵,问你 \(a\) 是否可以在进行有限次操作后变为 \(b\)

\((1≤t≤10^3), (1≤n≤10^{3})\)

思路:

\(a\)\(b\)矩阵异或成\(c\)矩阵,将问题转换一下,矩阵\(c\)是否能变成\(0\)矩阵。

感觉是个脑筋急转弯题,仔细思考,某行或某列最多操作一次(多的话无意义)。

假设现在将第一行变成全\(0\),于是对若干列进行了操作,记录下这些列操作次数。

接下来从第二行开始遍历,那么如果下面有某行不能达到全\(1\)或者全\(0\),那么这行就一定要修改列,但如果修改列就会把前几行的状态修改掉,所以某行最后一定是全\(1\)或者全\(0\)

将第一列变成全\(0\)同理。

时间复杂度:\(O(n^2)\)

Code:

int qu[1001][1001];
int qu1[1001][1001];
int qu2[1001][1001];
char s2[1001][1001];
char s3[1001][1001];
int lie[1001];
int hang[1001];
int main(){
    int t;
    srand(time(NULL));
    scanf("%d",&t);
    //t=1;
    while(t--){
        cin>>n;
        for(int i=1;i<=n;++i){
            hang[i]=lie[i]=0;
        }
        rep(i,1,n)cin>>s2[i]+1;
        rep(i,1,n)cin>>s3[i]+1;
        rep(i,1,n){
            rep(j,1,n){
                qu[i][j]=s2[i][j]-‘0‘;
                qu1[i][j]=s3[i][j]-‘0‘;
            }
        }
        int fg=1;
        rep(i,1,n){
            rep(j,1,n){
                qu[i][j]^=qu1[i][j];
                qu2[i][j]=qu[i][j];
            }
        }
 
        for(int i=1;i<=n;++i){
            if(qu[1][i]){
                qu[1][i]=0;
                lie[i]++;
            }
        }
        for(int i=2;i<=n;++i){
            for(int j=1;j<=n;++j){
                qu[i][j]=(qu[i][j]+lie[j])%2;
            }
        }
        for(int i=1;i<=n;++i){
            int cnt=0;
            for(int j=1;j<=n;++j){
                if(qu[i][j])cnt++;
            }
            if(cnt!=0&&cnt!=n)fg=0;
        }
        if(fg){
            puts("YES");
            continue;
        }
        for(int i=1;i<=n;++i){
            if(qu2[i][1]){
                qu2[i][1]=0;
                hang[i]++;
            }
        }
        for(int i=2;i<=n;++i){
            for(int j=1;j<=n;++j){
                qu2[j][i]=qu2[j][i]+hang[i];
                qu2[j][i]%=2;
            }
        }
        for(int i=1;i<=n;++i){
            int cnt=0;
            for(int j=1;j<=n;++j){
                if(qu2[i][j])cnt++;
            }
            if(cnt!=0&&cnt!=n)fg=0;
        }
        for(int i=1;i<=n;++i){
            hang[i]=lie[i]=0;
        }
        if(fg)puts("YES");
        else puts("NO");
    }
 
    return 0;
}

G. Strange Beauty

题意:

\(t\)组样例,有 \(n\) 个数,从中挑选一个最大的子集,使得集合中任意两个不同的数 \(x,y\) , 有 \(x|y\)\(y|x\)

\((1≤t≤10), (1≤n,a_i≤2*10^{5})\)

思路:

易知,整除具有传递性,\(a|b\)\(b|c\),则\(a|c\)

考虑\(dp\)求解,\(dp[i]\)表示以\(i\)为最大元素且以\(i\)结尾的子集最多个数。

\(dp[i]=max(dp[j])+cnt[i], (j|i,j<i)\)

从小到大枚举每个数的因子转移,最后注意边界即可。

时间复杂度:\(O(nlogn)\)

Code:

int main(){
    int t;
    srand(time(NULL));
    scanf("%d",&t);
    //t=1;
    while(t--){
        cin>>n;
        ll mx=0;
        rep(i,1,n){
            cin>>a[i];
            mx=max(a[i],mx);
        }
        ll ans=1e18;
        unordered_map<int,int>mp;
        rep(i,1,n)mp[a[i]]++;
        rep(i,1,mx){
            dp[i]=0;
        }
        rep(i,1,mx){
            dp[i]=max(dp[i],mp[i]+dp[1]);
            for(int j=2;j*j<=i;++j){
                if(i%j==0){
                    dp[i]=max(dp[i],mp[i]+dp[j]);
                    dp[i]=max(dp[i],mp[i]+dp[i/j]);
                }
            }
        }
        ll maxx=0;
        rep(i,1,mx){
            maxx=max(maxx,dp[i]);
        }
        cout<<n-maxx<<"\n";
    }
    return 0;
}

Codeforces Round #697 (Div. 3) A-G

标签:pos   struct   The   rand   水平   开始   number   std   als   

原文地址:https://www.cnblogs.com/quuns/p/14397720.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!