Codeforces Round #697 (Div. 3) A-G
作者:互联网
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;
}
标签:10,697,int,ll,Codeforces,cin,Div,rep,1001 来源: https://www.cnblogs.com/quuns/p/14397720.html