Codeforces Round #885 数学专场
妙,我只能说妙。今天补DEF发现除了F诡秘的杨辉三角,我都能独立做出来。
但为什么我感觉DE难度不如C!!!!
A
题意:一个人站在
题解:
对于永远等敏感字眼,我们一般是找题目中的不变量亦或者变量之间的关系。
在这个题中,注意到每移动一步之后,每个人的横纵坐标和的奇偶性都会改变,所以若两个人最初坐标奇偶性不同,则永远无法相遇,反过来,两个人最初坐标一致,则无论前者怎么走,后者都有走法使得曼哈顿距离不增大,而前者撞墙之后,后者就会缩小距离,最终相遇。
所以若存在
int n,x,y,k,m,a[105][105];
signed main(){
int t;cin>>t;
while(t--){
cin>>n>>m>>k>>x>>y;
int tag=0,s=(x+y)&1;
for(int i=1;i<=k;i++){
int x,y;cin>>x>>y;
if(((x+y)&1)==s)tag=1;
}
if(tag)cout<<"No\n";
else cout<<"Yes\n";
}
}
B
题意:有
“最大的最小”却不是二分答案,有意思。
在本题中,由于每次只能跳同一个颜色,必然是每一个颜色单独处理。
先将所有点按颜色为第一关键字,下标为第二关键字进行排序,将同色木板分成一组,考虑单独处理这一组。
设这一组木板下标分别为
记
先考虑不重新涂色,答案为
那么对于每个颜色都这样处理,最后取最小就是答案。
#define N 505050
#define int long long
#define pr pair<int,int>
#define mk make_pair
int n,x,y,k,m,pre[N],cnt[N];
pr c[N];
int solve(int l,int r){
int tot=0;
for(int i=l;i<=r;i++)cnt[++tot]=c[i].second;cnt[++tot]=n+1;
int ans=0,mx=0,cmx=0;
for(int i=tot;i;i--)cnt[i]=cnt[i]-cnt[i-1];
for(int i=1;i<=tot;i++){
mx=max(mx,cnt[i]);
}
for(int i=1;i<=tot;i++){
if(mx==cnt[i]){
cnt[i]=(cnt[i]+1)/2;mx=-1;
}
ans=max(ans,cnt[i]);
}
return ans-1;
}
signed main(){
int t;cin>>t;
while(t--){
read(n);int ans=0x3f3f3f3f,s;read(s);
for(int i=1;i<=n;i++)read(c[i].first),c[i].second=i;
sort(c+1,c+n+1);int l=0,r=0;c[0]=c[n+1]=mk(0,0);
for(int i=1;i<=n;i++){
if(c[i].first!=c[i-1].first)l=i;
if(c[i].first!=c[i+1].first){
ans=min(ans,solve(l,i));
}
}
cout<<ans<<"\n";
}
}
C
题意:
给定数组
令
求是否可能通过
首先,可以发现每一个
则我们可以考虑求出对于每一个
现在我们来求解
设
注意到若
不难发现规律,若
这个过程类似于欧几里得算法,不难写出如下递归函数:
int gcd(int a,int b){
if(a==0)return 0;
if(b==0)return 1;
if(a>=b){
int r=a%b,k=a/b;
if(k%2==1)return gcd(b,r)+k+k/2;
else return gcd(r,b)+k+k/2;
}
return 1+gcd(b,abs(a-b));
}
注意到在最后
则有通解的充要条件显然是所有的
注意
D
找循环节,不变量是解决数学题的有力手段
题意:
给定数
- 将
加上其个位数字。 - 将
累加到答案中。
求答案的最大值。
贪心策略:所有操作一必然是在最开始进行的,证明显然。
考虑特殊性质:
,此时最多进行一次操作1。 为偶数,此时 的个位数字随着不断地操作1最后回归到 的循环中。 为奇数且不等于 ,此时进行一次操作1即可化为情况2。
对于第一种情况,答案显然为
对于第二种情况。设最优情况下操作1进行的次数
第三种情况可以转化为第二种情况,在此略去。
#define int long long
signed main(){
int t,a,k;
cin>>t;
while(t--){
cin>>a>>k;
if(a%10==0){
cout<<a*k<<"\n";continue;
}
if(a%10ll==5ll){
cout<<max(a*k,(a+5ll)*(k-1ll))<<"\n";continue;
}
int ans=a*k;
if(a&1ll){
a+=a%10ll;k--;ans=max(ans,a*k);
}
for(int i=0;i<4;i++){
if(!k)break;
int aa=-80ll,bb=20ll*k-4ll*a,cc=a*k,x=-1*bb/(2ll*aa);
if(x<0)x=0;
x=min(x,k/4);
ans=max(ans,x*x*aa+bb*x+cc);
if((x+1)*4<=k){
x++;ans=max(ans,x*x*aa+bb*x+cc);
}
a+=a%10ll;k--;
}
cout<<ans<<"\n";
}
}
E
挺有意思的问题,但个人觉得赛场上开题顺序如果是 ABEDCF 可能就不会是现在这悲催的结局了。
注意到:
这里长得有点像约数,但不完全是约数。
注意到
则
考虑如何求出
直接求显然不现实,可以考虑维护每个质因子出现次数
这是一个递推的过程,可以对
#include<iostream>
using namespace std;
#define N 1005050
#define int long long
int f[N],n,p,x,ans=1;
int power(int a,int b){
int ans=1;
while(b){
if(b&1)ans=ans*a%p;
a=a*a%p;
b>>=1;
}
return ans;
}
int solve(int d){
while((d&1)==0)d>>=1;
for(int i=2;i*i<=d;i++){
int cnt=0;
while(d%i==0){
// cout<<"prime: "<<i<<"\n";
d/=i;++cnt;
}
if(cnt){
if(!f[i]){
ans=ans*(cnt+1)%p;f[i]=cnt;
}
else ans=ans*power(f[i]+1,p-2)%p*(f[i]+cnt+1)%p,f[i]+=cnt;
}
}
if(d>1){
if(d<=1e6){
int i=d,cnt=1;
if(!f[i]){
ans=ans*(cnt+1)%p;f[i]=cnt;
}
else ans=ans*power(f[i]+1,p-2)%p*(f[i]+cnt+1)%p,f[i]+=cnt;
}
else ans=ans*2ll%p;
}
return ans;
}
signed main(){
cin>>x>>n>>p;
solve(x);
for(int i=1;i<=n;i++){
int d;cin>>d;solve(d);
cout<<ans<<"\n";
}
}
但是交上去挂了,为什么?
模数不固定,虽然是质数但可能很小,极有可能不存在逆元。
怎么办?发现我们只需要维护
注意这里的时间复杂度是不正确的(
这里容易被大质数卡得分解过程爆炸,可以特判一下。
#pragma GCC opzitime(3)
#include<iostream>
using namespace std;
#define N 1005050
#define int long long
int f[N],n,p,x,ans=1,pri[N],v[N],tot,id[N];
struct node{
int l,r,mul;
}t[N<<2];
#define lc x<<1
#define rc x<<1|1
void read(int &x){
x=0;char ch=getchar();
while(ch>'9'||ch<'0'){
ch=getchar();
}
while('0'<=ch&&ch<='9'){
x=(x<<3)+(x<<1)+(ch^48);ch=getchar();
}
}
void build(int x,int l,int r){
t[x].l=l,t[x].r=r,t[x].mul=1;
if(l==r)return ;
int mid=l+r>>1;
build(lc,l,mid);
build(rc,mid+1,r);
}
void updata(int x,int pos,int k){
if(t[x].l==t[x].r){
t[x].mul+=k;
t[x].mul%=p;
return ;
}
if(t[lc].r>=pos)updata(lc,pos,k);
else updata(rc,pos,k);
t[x].mul=t[lc].mul*t[rc].mul%p;
}
void solve(int d){
while((d&1)==0)d>>=1;
if(d<=1e6&&v[d]==0){
updata(1,id[d],1);return ;
}
for(int i=2;i<=tot;i++){
int cnt=0;
while(d%pri[i]==0){
d/=pri[i];++cnt;
}
updata(1,i,cnt);
if(d<pri[i])break;
if(pri[i]*pri[i]>d){
if(d>1e6)ans=2;
else if(v[d]==0){
updata(1,id[d],1);
}
break;
}
}
}
void init(){
v[1]=1;
for(int i=2;i<=1e6;i++){
if(!v[i]){
// cout<<"pri: "<<i<<"\n";
pri[++tot]=i;id[i]=tot;
}
for(int j=i;j<=1e6;j+=i){
if(i!=j)v[j]=1;
}
}
}
signed main(){
read(x),read(n),read(p);
init();
build(1,1,tot);
solve(x);
for(int i=1;i<=n;i++){
int d;read(d);solve(d);
printf("%lld\n",ans*t[1].mul%p);
}
}
F
现在以
……
如果说,我们写一个系数矩阵
观察到什么了吗?
而在最后一行,变成了自己异或自己,则必然有解。-1就是骗人的。。。
我们考虑求出这个最小的解。显然当全部为0之后,后面无论执行多少次操作都是全0。
那么这个值是可二分的,换句话说是可倍增的(题目更适用倍增一些)。
我们考虑从大到小枚举增量
考虑求出上一次操作到
然后判断是否全0即可解决这个问题。复杂度
有没有什么优化空间呢?
注意到每次“有效”的倍增更改后,
时间复杂度
#include<iostream>
using namespace std;
#define N (1<<21)
int a[N],b[N],n;
int main(){
ios::sync_with_stdio(false);
cin>>n;for(int i=0;i<n;i++)cin>>a[i];
int mx=0;for(int i=0;i<n;i++)mx=max(mx,a[i]);
if(!mx){
cout<<"0\n";return 0;
}
int ans=0;
while(n>1){
int m=n>>1;
for(int i=0;i<m;i++)b[i]=a[i]^a[i+m];
mx=0;for(int i=0;i<m;i++)mx=max(mx,b[i]);
if(mx){
ans|=m;for(int i=0;i<m;i++)a[i]=b[i];
}
// else break;
n=m;
}
cout<<ans+1<<"\n";
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!