CF选做
CF1895C
方法:数位dp
使用f[i][j]表示长串+短串的情况下,长串分割成两部分,且
数位数差位i,数位和差为j的方案数。
通过预处理出f数组得到答案
#include <stdio.h> #include <string.h> #include <math.h> #define N 400000 #define ll long long //typedef long long ll ll f[200][200],g[200][200]; ll a[N],b[N],c[N]; ll n,ans; int main() { scanf("%lld",&n); for(int i=1;i<=n;++i) { scanf("%lld",&a[i]); int x=a[i],y=1; while(x) {++b[i];c[i]+=x%10;x/=10;y*=10;} x=a[i]*10;int now=c[i]; for(int j=0;j<=b[i];++j) { now=now-x%10*2; if(b[i]-j*2<=0||now<=0) break; f[b[i]-j*2][now]++; x/=10; }//the first is no shorter than the latter y/=10;now=c[i]; for(int j=1;j<=b[i];++j) { now=now-a[i]/y%10*2;y/=10; if(b[i]-j*2<=0||now<=0) break; g[b[i]-j*2][now]++; }//the latter is shorter than the first one } for(int i=1;i<=n;++i) { ans+=f[b[i]][c[i]]+g[b[i]][c[i]]; } printf("%lld\n",ans); return 0; }
CF1895D
数位贪心,首先通过异或的性质得到,设c[i]=a[1]...a[i]则b[i]=b[0]^c[i]
然后只需要找到合适的b[0]即可。
因为题目保证有解,所以c[i]不可能有两个相同的元素(否则就无解),这样排除了重复的情况就只剩下b[0]^c[i]>=n的情况了
之后考虑每一位,对每一位进行贪心,统计c数组所有数的第i位,如果1比0多就让b[0]=1,这样可以最小化b数组所有数的总和,从而最小化其最大值。
#include<iostream> #include<cstdio> #include<cmath> using namespace std; const int N = 400000; int n,m,ans; int a[N],b[N],c[N]; int main() { scanf("%d",&n); for(int i=1;i<n;++i) { scanf("%d",&a[i]); c[i]=c[i-1]^a[i]; } for(int i=0;i<=31;++i) { int tmp=1<<i,num=0; for(int j=1;j<n;++j) { if(c[j]&tmp) ++num; } if(num>n/2) ans|=tmp; } for(int i=0;i<n;++i) // printf("%d ",c[i]); printf("%d ",ans^c[i]); return 0; }
CF1899D
只想到了化成
思路1 分析图象,发现只有x=1或x=2时等式成立
思路2 设b>a,则可设
map的使用
map<double,string> ha;//定义map ha x;x.first=1.2;x.second="abc";//单点调用 ha[3.4]++;//用键调用值 for(auto i : ha){ printf("%lf\n%s\n",i.first,i.second); }//遍历map
#include<cstdio> #include<iostream> #include<cmath> #include<map> #define eps 0.00006 #define ll long long using namespace std; map<int,ll>m; int main() { int t;scanf("%d",&t);while(t--){ ll n,ans=0;scanf("%lld",&n); /**for(int i=1;i<=n;++i) { double x;scanf("%lf",&x); if(x==1) x=2; a[i]=x*log(2)-log(x*1.0); //log() means ln,log_e //Dialog:double is not big enough /* for(int j=1;j<i;++j) if(abs(a[i]-a[j])<eps) ++ans; //TLE,O(n^2) is too slow Sol1:Sort,then scan the same numbers Sol2:Use map * / ans+=m[a[i]*1000000]; m[a[i]*1000000]++; }*/ for(int i=1;i<=n;++i) { int x;scanf("%d",&x); if(x==1) x=2; ans+=m[x]; m[x]++; } printf("%lld\n",ans); m.clear(); } return 0; }
CF1896D
非常好一道题,使我放弃python
python中没有C++中set和map的内置红黑树,而这道题可能需要用到
首先发现结论:如果存在区间和 s 可以取到,那么 s-2 就一定可以取到,因此与数列总和 sum 奇偶性相同的 s 肯定能取到。
与 sum 奇偶性不同的 s 只需要判断离两端最近的 1 有多近就行。
(证明:分情况讨论:取到s时两端有至少一个 2 或者 有两个 1 ,从而得到 s-2 能取到)
然而维护离两端最近的 1 不能用散列表,否则每次查询最坏复杂度为 O(n) ,总复杂度O(Tmn)会在第9个点上t掉,需要一种数据结构支持单点插入和单点删除并维护整个数列的最值——红黑树
set的用法
//https://zhuanlan.zhihu.com/p/682656691 #include<cstdio> #include<set> using namespace std; set<int>b; int main() { int n;scanf("%d",&n); //set的基本功能 b.clear();//清空 for(int i=1;i<=n;++i) { int x;scanf("%d",&x); b.insert(x);//插入 } printf("%d %d %d\n",*b.begin(),*b.rbegin(),*b.size()); //最小值b.begin(),最大值b,rbegin() //而b.end()返回的是最大值后面的迭代器,b.rend是最小值后面的迭代器 //set查找: auto it=b.find(3); if (it!=b.end()){ cout<<"Found: "<<*it<<"\n"; }else{ cout<<"Not found.\n"; }//找到返回地址,没找到返回b.end() //set的遍历: for (auto it = b.begin(); it != b.end(); ++it) { cout << *it << " "; } return 0; }
#include<cstdio> #include<iostream> #include<cmath> #include<set> using namespace std; int n,m,sum,a[200000]; set<int> b; int mini() { if(b.empty()) return n; return min(*b.begin()-1,n-*b.rbegin()); } void judge(int s) { if(s%2==sum%2&&sum>=s) { printf("YES\n"); return; } if(sum-2*mini()-1>=s) { printf("YES\n"); return; } printf("NO\n"); } void doit() { sum=0;b.clear(); scanf("%d%d",&n,&m); for(int i=1;i<=n;++i) { scanf("%d",&a[i]); sum+=a[i]; if(a[i]==1) b.insert(i); } for(int i=1;i<=m;++i) { int op;scanf("%d",&op); if(op==1) { int s;scanf("%d",&s); judge(s); } else { int u,v;scanf("%d%d",&u,&v); if(a[u]==1&&v==2) { a[u]=2; b.erase(u); ++sum; } else if(a[u]==2&&v==1) { a[u]=1; b.insert(u); --sum; } } } } int main() { int t;scanf("%d",&t); while(t--){ doit(); } return 0; }
CF1903C
维护后缀和并在后缀和上找规律,观察到如果第i+1个数的后缀和非负,那么在第i个和第i+1个之间分组一定不会使总和变小。
注意先观察后写代码,不要在细节上浪费时间
注意开ll
#include<cstdio> #include<iostream> #define ll long long using namespace std; int n; ll a[200000],sub[200000]; int main() { int t;scanf("%d",&t);while(t--){ scanf("%d",&n); for(int i=1;i<=n;++i) scanf("%lld",&a[i]); sub[n]=a[n]; for(int i=n-1;i>=1;--i) sub[i]=sub[i+1]+a[i]; ll num=0,tmp=0,ans=0; for(int i=1;i<=n;++i) { tmp+=a[i]; if(sub[i+1]>=0) { ++num; ans+=tmp*num; tmp=0; continue; } } if(num) ans+=tmp*num; else ans+=tmp; printf("%lld\n",ans); } }
CF1948D
经典前缀后缀优化,通过预处理任意两个后缀的最长公共前缀来
#include<cstdio> #include<iostream> #include<cstring> #define ll long long using namespace std; char a[10000]; int f[6000][6000]; int main() { int t;scanf("%d\n",&t);while(t--){ scanf("%s",a);int n=strlen(a),ans=0; for(int i=n-1;i>=0;--i) for(int j=n-1;j>=0;--j) if(a[i]==a[j]||a[i]=='?'||a[j]=='?') f[i][j]=f[i+1][j+1]+1; for(int l=0;l<n;++l){ for(int r=l;n-r>=r-l+2;++r){ if(f[l][r+1]==r-l+1) ans=max(ans,2*(r-l+1)); } } printf("%d\n",ans); for(int i=n-1;i>=0;--i) for(int j=n-1;j>=0;--j) f[i][j]=0; } return 0; }
CF1907D
二分答案,注意二分答案的边界一定是[0,inf] ,而不是区间间距的边界!
#include<cstdio> #include<iostream> #include<cstring> #include<cmath> using namespace std; const int N=400000; const int inf=1000000000; int l[N],r[N]; int n; int f(int k){ int nowl=0,nowr=0; for(int i=1;i<=n;++i){ if(l[i]-nowr>k||nowl-r[i]>k) return 0; nowl=max(l[i],nowl-k); nowr=min(r[i],nowr+k); } return 1; } int main() { int t;scanf("%d\n",&t);while(t--){ scanf("%d",&n); for(int i=1;i<=n;++i) scanf("%d%d",&l[i],&r[i]); int ll=0,rr=inf; while(ll<rr){ int mid=(ll+rr)/2; if(f(mid)) rr=mid; else ll=mid+1; } printf("%d\n",ll); } return 0; }
CF1944B
有时候感觉写的很不自信的大模拟,捋顺一点也就能写出来了
#include<cstdio> #include<iostream> #include<vector> #include<map> #define ll long long using namespace std; const int N = 100000; int a[N],b[N]; map<int,int> m; vector<int> v,va,vb; int main() { int T;cin>>T;while(T--){ int n,k,cnt=0,cnta=0,cntb=0;cin>>n>>k;m.clear();v.clear();va.clear();vb.clear(); for(int i=1;i<=n;++i) cin>>a[i]; for(int i=1;i<=n;++i) cin>>b[i]; for(int i=1;i<=n;++i) m[a[i]]++; for(int i=1;i<=n;++i) m[b[i]]--; for(int i=1;i<=n;++i){ if(m[a[i]]==2) { va.push_back(a[i]);++cnta;m[a[i]]=N; } if(m[b[i]]==-2){ vb.push_back(b[i]);++cntb;m[b[i]]=N; } if(m[a[i]]==0){ ++cnt;v.push_back(a[i]);m[a[i]]=N; } } for(int i=0;i<cnta&&i<k;++i){ cout<<va[i]<<' '<<va[i]<<' '; } for(int i=0;i<(k-cnta)*2;i+=2){ cout<<v[i]<<' '<<v[i+1]<<' '; }cout<<endl; for(int i=0;i<cntb&&i<k;++i){ cout<<vb[i]<<' '<<vb[i]<<' '; } for(int i=0;i<(k-cnta)*2;i+=2){ cout<<v[i]<<' '<<v[i+1]<<' '; }cout<<endl; } return 0; }
CF1944C
看了hint才做出来
Alice can adapt to Bob's strategy. Try to keep that in mind.
每个数分出现0次、1次和多次三种情况
#include<cstdio> #include<iostream> #include<map> #include<algorithm> #define ll long long using namespace std; const int N = 400000; int a[N]; map<int,int> m; int main() { int T;cin>>T;while(T--){ int n,b=0;cin>>n;m.clear(); for(int i=1;i<=n;++i) { int x;cin>>x; m[x]++; } for(int i=0;;++i){ if(!m[i]){ cout<<i<<endl; break; } if(m[i]==1){ if(b){ cout<<i<<endl; break; } b=1; } } } return 0; }
CF1955D
灵活运用map或set计算滑动窗口中每个值的贡献,居然能做到
#include<cstdio> #include<iostream> #include<algorithm> #include<map> #define ll long long using namespace std; const int N=300000; int a[N],b[N]; map<int,int>ma; int main(){ int T;cin>>T;while(T--){ int n,m,k,num=0,ans=0;cin>>n>>m>>k;ma.clear(); for(int i=1;i<=n;++i) cin>>a[i]; for(int i=1;i<=m;++i) {cin>>b[i];ma[b[i]]+=1;} for(int i=1;i<=m;++i){ if(ma.find(a[i])!=ma.end()){ --ma[a[i]]; if(ma[a[i]]>=0) ++num; } }if(num>=k) ++ans; for(int i=m+1;i<=n;++i){ if(ma.find(a[i-m])!=ma.end()){ ++ma[a[i-m]]; if(ma[a[i-m]]>0) --num; } if(ma.find(a[i])!=ma.end()){ --ma[a[i]]; if(ma[a[i]]>=0) ++num; } if(num>=k) ++ans; } cout<<ans<<endl; } return 0; }
CF1955E
不知道为什么不能线性做,这不核理
不能二分,因为如果n符合题意n-1不一定符合
正解就是在暴力判断上加一个优化,通过异或差分实现
#include<cstdio> #include<iostream> #include<algorithm> #define ll long long using namespace std; int n;string a; int tmp[10000]; int f(int x){ int now=a[0]-'0';tmp[0]=a[0]-'0'; for(int i=1;i<n;++i) tmp[i]=(a[i-1]-'0')^(a[i]-'0'); for(int i=0;i<n;now^=tmp[++i]){ if(now==1) continue; if(i+x>n) return 0; tmp[i]^=1;now^=1; if(i+x<n) tmp[i+x]^=1; } return 1; } int main(){ int T;cin>>T;while(T--){ cin>>n>>a; for(int l=n;l>=1;--l) if(f(l)){ cout<<l<<endl; break; } } return 0; }
CF1957C
首先发现已经占有的格子的坐标与最终答案无关,可以把它转化成m*m的方格进行分析,然后发现如果放在对角线上的格子就减少一行一列,其他减少两行两列。设有n-i个对角线格子放上了棋子(方法数为
需要预处理阶乘
#include<bits/stdc++.h> #define int long long using namespace std; const int mod=1000000007; int f[400000]; void init(){ f[0]=1; for(int i=1;i<=300000;++i){ f[i]=f[i-1]*i%mod; } } int re(int x){ int tmp=mod-2,ans=1; while(tmp){ if(tmp&1) ans=ans*x%mod; x=x*x%mod;tmp/=2; } return ans; } int c(int n,int m){ int num=1,den=1; // for(int i=n-m+1;i<=n;++i) num=(num*i)%mod; // for(int i=1;i<=m;++i) den=(den*i)%mod; num=f[n];den=f[m]*f[n-m]%mod; return (num*re(den))%mod; } int g(int x){ // if(!x) return 1; int ans=1,den=1; // for(int i=x;i>1;--i) ans=ans*i%mod; // for(int i=x/2;i>1;--i) den=den*i%mod; ans=f[x];den=f[x/2]; return ans*re(den)%mod; } signed main(){ init(); int T;cin>>T;while(T--){ int n,k,tmp=0,ans=0;cin>>n>>k; while(k--){ int A,B;cin>>A>>B; if(A==B) tmp+=1;else tmp+=2; }n-=tmp; for(int i=0;i<=n;i+=2){ ans=(ans+c(n,i)*g(i))%mod; } cout<<ans<<endl; } return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效