写在前面的话
这场比赛打了两个半小时过了7题,剩下3题没时间开了。题目质量还是非常高的,不过难度偏大了一点。。
A
知识点:位运算
题意是求不大于x的数中,有多少数和a没有两个数位都是1。
先转为二进制,考虑a的每一位:
首先如果a的当前位是1,那么待选择的数当前位只能选择0,没有其他选项。
如果a的当前位是0,那么根据x的当前位分类讨论:
如果x当前位是1,那么对于待选择的数y而言,当前位若选择0则有的贡献(其中sum为后面a的二进制的0的数量),若选择1则继续考虑下一位。
如果x当前位是0,那么当前的数只能选0。
然后处理一下尾部的特判就可以了。
(这道题调了好久才过qwq)
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int mod =1e9+7;
ll power(ll a,ll b){
ll res=1;
while(b){
if(b&1)res=res*a%mod;
b>>=1;
a=a*a%mod;
}
return res;
}
ll inv(ll x){
return power(x,mod-2);
}
ll bs(ll l,ll r,ll n){
ll mid=l+r>>1;
if(l==r)return l;
if(mid*(mid+1)/2<=n)return bs(mid+1,r,n);
return bs(l,mid,n);
}
int main(){
// cout<<inv(2);
// std::ios::sync_with_stdio(false);
ll t,n,i,j;
cin>>t;
while(t--){
ll a,x;
cin>>a>>x;
ll p=x;
ll sum[32]={0},s=0,aa[32]={0},xx[32]={0};
for(i=0;i<32;i++){
aa[i]=a%2;
xx[i]=x%2,x/=2;
sum[i]=s+=a%2==0;
a/=2;
}
ll res=0;
for(i=31;i>=0;i--){
if(xx[i])break;
}
if(sum[i]==i+1){
cout<<p<<endl;
continue;
}
for(;i>0;i--){
if(xx[i]==1){
if(aa[i]==1){
res+=1<<sum[i-1]; //我莫得选择,只能选0。后面随便选
break;
}
res+=1<<sum[i-1]; //若当前选0
//继续下一个
}
else{
//我莫得选择,只能选0
}
}
if(i==0){
res++;
if(aa[0]==0&&xx[0]==1)res++;
}
cout<<res-1<<endl;
}
}
B
知识点:大模拟
模拟就完事了。我的方法是先把每个数字对应的点和井号看成01转为二进制,然后处理就相对方便一点。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int mod =1e9+7;
ll power(ll a,ll b){
ll res=1;
while(b){
if(b&1)res=res*a%mod;
b>>=1;
a=a*a%mod;
}
return res;
}
ll inv(ll x){
return power(x,mod-2);
}
ll bs(ll l,ll r,ll n){
ll mid=l+r>>1;
if(l==r)return l;
if(mid*(mid+1)/2<=n)return bs(mid+1,r,n);
return bs(l,mid,n);
}
string a[501];
map<ll,int>m;
int f(string s){
int i,res=0;
for(i=0;i<s.length();i++){
res*=2;
res+=s[i]-'0';
}
return res;
}
string d[10]={"111111000111111",
"111110000000000",
"101111010111101",
"111111010110101",
"111110010000111",
"111011010110111",
"111011010111111",
"111110000100111",
"111111010111111",
"111111010110111"};
string add="001000111000100";
int main(){
// cout<<inv(2);
// std::ios::sync_with_stdio(false);
ll t,n,i,j,k;
cin>>t;
for(i=0;i<10;i++){
m[f(d[i])]=i;
}
ll ad=f(add);
while(t--){
ll temp=0,res=0;
for(i=0;i<5;i++)cin>>a[i];
for(i=2;i<a[0].length();i+=4){
ll ttemp=0;
for(k=i;k>=i-2;k--)
for(j=4;j>=0;j--){
ttemp*=2;
if(a[j][k]=='#')ttemp++;
}
if(ttemp==ad)res+=temp,temp=0;
else temp*=10,temp+=m[ttemp];
}
res+=temp;
// cout<<res<<endl;
int out[20]={0};
for(i=0;i<14;i++)out[i]=res%10,res/=10;
while(i>=0&&out[i]==0)i--;
if(i==-1)i++;
char pr[5][500];
k=2;
int z;
for(;i>=0;i--){
int ttemp=0;
for(z=k;z>=k-2;z--){
for(j=4;j>=0;j--){
if(d[out[i]][ttemp++]=='1')pr[j][z]='#';
else pr[j][z]='.';
}
}
if(i==0){
for(j=4;j>=0;j--)pr[j][k+1]='\0';
}
else{
k++;
for(j=4;j>=0;j--)pr[j][k]='.';
k+=3;
}
}
for(i=0;i<5;i++)printf("%s\n",pr[i]);
cout<<endl;
}
}
D
可以发现,经过一定数量的操作后一定能产生(0,0)的点,所以统计所有点数量就可以了。
ps.这道题刚开始以为是必须满足x|y=y的情况才可以,然后推了半天dp没推出来,浪费了好多时间。。。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int mod =1e9+7;
ll power(ll a,ll b){
ll res=1;
while(b){
if(b&1)res=res*a%mod;
b>>=1;
a=a*a%mod;
}
return res;
}
ll inv(ll x){
return power(x,mod-2);
}
ll f(ll x,ll y){ //0 0到x y
return (x+1)*(y+1);
}
int main(){
// cout<<inv(2);
// std::ios::sync_with_stdio(false);
ll t,n,i,j;
cin>>t;
while(t--){
ll x,y,x1,y1;
cin>>x>>y>>x1>>y1;
cout<<f(x1,y1)-f(x-1,y1)-f(x1,y-1)+f(x-1,y-1)<<endl;
}
}
F
首先,一次能消完的一定是形如 的数。
那么可以先二分出小于等于n的最大的这种形式,那么令 ,
,则n每次要么减p1然后加n 要么减p2然后加n,这个问题就是看什么时候能回到n(完成一次循环)
我们发现,不管是减p1加n,还是减p2加n,最后一定是形成p2-p1的一个剩余系,并且这个剩余系要通过操作n的方式得出,所以最终能形成的就是 (或者
)这么多个数。
这也是循环的大小,即所求的解。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int mod =1e9+7;
ll power(ll a,ll b){
ll res=1;
while(b){
if(b&1)res=res*a%mod;
b>>=1;
a=a*a%mod;
}
return res;
}
ll inv(ll x){
return power(x,mod-2);
}
ll bs(ll l,ll r,ll n){
ll mid=l+r>>1;
if(l==r)return l;
if(mid*(mid+1)/2<=n)return bs(mid+1,r,n);
return bs(l,mid,n);
}
int main(){
// cout<<inv(2);
// std::ios::sync_with_stdio(false);
ll t,n,i,j;
cin>>t;
while(t--){
cin>>n;
ll so=bs(1,1e9,n)-1;
ll p=so*(so+1)/2,pp=(so+2)*(so+1)/2;
if(p==n)cout<<1<<endl;
else{
ll cha1=n-p,cha2=pp-p;
// cout<<p<<" "<<n<<" "<<pp<<endl;
ll g=__gcd(cha1,cha2);
cout<<cha2/g<<endl;
}
}
}
G
这道题求 的最大k。由于y小于1e18,而2的64次方已经爆longlong了,所以当x大于1的时候模拟就可以了。
但还是要注意爆longlong的问题。可以写大数或者__int128,但我为了省事就直接上py了(
t=int(input())
while t>0:
t-=1
x,y=map(int,input().split())
if y<=0 or x<=1:
print(-1)
else:
res=1
p=x
while p<=y:
p*=x
res+=1
print(res-1)
H
模拟就可以了,看对称的字符串能不能取到相同的字符,只要有一个取不到就gg
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int mod =1e9+7;
ll power(ll a,ll b){
ll res=1;
while(b){
if(b&1)res=res*a%mod;
b>>=1;
a=a*a%mod;
}
return res;
}
ll inv(ll x){
return power(x,mod-2);
}
int main(){
// cout<<inv(2);
// std::ios::sync_with_stdio(false);
ll t,n,i,j;
cin>>t;
while(t--){
string s[1000];
cin>>n;
for(i=0;i<n;i++)cin>>s[i];
int jud=0;
for(i=0;i<n/2;i++){
int tong1[26]={0},tong2[26]={0};
for(j=0;j<s[i].length();j++)tong1[s[i][j]-'a']++;
for(j=0;j<s[n-i-1].length();j++)tong2[s[n-i-1][j]-'a']++;
for(j=0;j<26;j++)if(tong1[j]&&tong2[j])break;
if(j==26)jud=1;
}
if(jud)cout<<"No\n";
else cout<<"Yes\n";
}
}
I
如果字符串所有字符都相同,输出0。
如果字符串不是回文,输出n
否则输出n-1即可。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int mod =1e9+7;
ll power(ll a,ll b){
ll res=1;
while(b){
if(b&1)res=res*a%mod;
b>>=1;
a=a*a%mod;
}
return res;
}
ll inv(ll x){
return power(x,mod-2);
}
int main(){
// cout<<inv(2);
// std::ios::sync_with_stdio(false);
string s;
int i;
cin>>s;
int jud=0;
for(i=1;i<s.length();i++){
if(s[i]!=s[i-1])jud=1;
}
if(!jud)cout<<0;
else{
for(i=0;i<s.length();i++){
if(s[i]!=s[s.length()-i-1])break;
}
if(i!=s.length())cout<<s.length();
else cout<<s.length()-1;
}
}

京公网安备 11010502036488号