2025牛客寒假算法基础集训营4 ptlks的题解
A.Tokitsukaze and Absolute Expectation
题意:
很纯粹的数学题
思路
问题可以转化为一个很经典的题,两区间内分别随机选取一个数,求它们差的绝对值的期望。
代码
点击查看代码
//Z是自动取模
Z f(Z a,Z b,Z c,Z d){
if(a>c){
swap(a,c);
swap(b,d);
}
if(b<=c){
return (c+d-a-b)/Z(2);
}
if(b<=d){
Z t=b-c+1;
return (c + d - a - b) / Z(2) + (t -t.inv()) / Z(3) * t / (b - a + 1) * t / (d - c + 1);
}else{
return f(a, d, c, d) * (d - a + 1) / (b - a + 1) + (b - c + 1) /Z(2) * (b - d) / (b - a + 1);
}
}
B.Tokitsukaze and Balance String (easy)
题意:
略
思路
n小于等于10,枚举即可。
代码
点击查看代码
略
C.Tokitsukaze and Balance String (hard)
题意:
略
思路
题目所求的平衡字符串其实只与字符串的首尾两个字符有关,我一开始也没发现,卡了我好久。
代码
点击查看代码
void solve() {
srand(time(0));
int n, sum = 0, ans = 1;
cin >> n;
cin >> s;
for (int i = 0; i < n; i++) {
if (s[i] == '?') {
sum++;
}else{
if(sum)ans*=qpow(2,sum-1),ans%=mod;
sum=0;
}
}
if(sum)ans*=qpow(2,sum-1),ans%=mod;
cout<<ans*n%mod<<endl;
}
D.Tokitsukaze and Concatenate Palindrome
题意:
给定两个字符串a,b,可以随意重排字符串,至少需要替换几次,才能使a+b成为回文串。
思路
因为可以随意重排,所以直接考虑各字符个数就行,考虑将a与b的字符一一对应,长的那个再处理一下就行了。
代码
点击查看代码
void solve() {
int n, m;
cin >> n>>m;
cin >> s1>>s2;
if(n>m){
swap(n,m);
swap(s1,s2);
}
for(int i=0;i<26;i++){
a[i]=b[i]=0;
}
for(int i=0;i<n;i++){
a[s1[i]-'a']++;
}
for(int i=0;i<m;i++){
b[s2[i]-'a']++;
}
int sum=n+m;
for(int i=0;i<26;i++){
int x=min(a[i],b[i]);
a[i]-=x;
b[i]-=x;
sum-=x*2;
}
int s=(n+m)/2-n;
for(int i=0;i<26;i++){
while(b[i]>1&&s){
s--;
b[i]-=2;
sum-=2;
}
}
cout<<(sum)/2<<endl;
}
E.Tokitsukaze and Dragon's Breath
题意:
求X覆盖的最大值
思路
直接记录即可,类似记录行列的和,一个以i+j记录,另一个以j-i记录。
代码
点击查看代码
void solve() {
int n,m;
cin>>n>>m;
map<int,int>hm1,hm2;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
int x;
cin>>x;
a[i][j]=x;
hm1[i+j]+=x;
hm2[j-i]+=x;
}
}
int ans=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
ans=max(ans,hm1[i+j]+hm2[j-i]-a[i][j]);
}
}
cout<<ans<<endl;
}
F.Tokitsukaze and Kth Problem (easy)
题意:
定义二元组对应的值 ,求全部不同的二元组中的前k大。
思路
有点复杂,思路是先找到第k大的值t,再记录比t大的数,剩下不够的再用t补。先对所有数模p,那么剩余的数两两相加,最大不超过2*(p-1)。则对于两个数的模p后的和u,我们只要找u和u+p两个值即可。我这边是先排序,再二分查找,写起来有一点麻烦,细节可能有点多。
代码
点击查看代码
void solve() {
int p, k;
cin >> n >> p >> k;
for (int i = 1; i <= n; i++) {
cin >> a[i];
// a[i] = 1;
a[i] %= p;
}
sort(a + 1, a + 1 + n);
int l = 0, r = p - 1;
int ans = -1, ss = 0;
while (l <= r) {
int mid = l + r >> 1;
int s = 0;
for (int i = 1; i <= n; i++) {
int p1 = lower_bound(a + i + 1, a + 1 + n, mid - a[i]) - a - 1;
int p2 = upper_bound(a + i + 1, a + 1 + n, p - 1 - a[i]) - a - 1;
// cout<<p1<<' '<<p2<<endl;
s += p2 - p1;
p1 = lower_bound(a + i + 1, a + 1 + n, p + mid - a[i]) - a - 1;
p2 = upper_bound(a + i + 1, a + 1 + n, p * 2 - 1 - a[i]) - a - 1;
s += p2 - p1;
// cout<<p1<<' '<<p2<<endl;
// cout<<s<<'\n';
}
// cout<<mid<<' '<<s<<endl<<endl;
if (s >= k) {
ans = mid;
ss = s;
l = mid + 1;
} else {
r = mid - 1;
}
}
vector<int>q;
// cout << ans << endl;
if (ans == -1) {
for (int i = 1; i <= n; i++) {
for (int j = i + 1; j <= n; j++) {
q.push_back((a[i] + a[j]) % p);
}
}
} else {
for (int i = 1; i <= n; i++) {
int p1 = lower_bound(a + i + 1, a + 1 + n, ans + 1 - a[i]) - a - 1;
int p2 = upper_bound(a + i + 1, a + 1 + n, p - 1 - a[i]) - a - 1;
for (int j = p1 + 1; j <= p2; j++) {
q.push_back((a[i] + a[j]) % p);
}
p1 = lower_bound(a + i + 1, a + 1 + n, p + ans + 1 - a[i]) - a - 1;
p2 = upper_bound(a + i + 1, a + 1 + n, p * 2 - 1 - a[i]) - a - 1;
for (int j = p1 + 1; j <= p2; j++) {
q.push_back((a[i] + a[j]) % p);
}
}
int l=q.size();
for (int i = 0; i < k - l; i++) {
q.push_back(ans);
}
}
sort(q.begin(), q.end(), greater<>());
for (int i = 0; i < k; i++) {
if (i < q.size()) {
cout << q[i] << " \n"[i == k - 1];
} else {
cout << -1 << " \n"[i == k - 1];
}
}
}
I.Tokitsukaze and Pajama Party
题意:
查找子串
思路
签到题。
代码
点击查看代码
略
J.Tokitsukaze and Shawarma
题意:
每次可以占领相邻的点,有k次机会占领任意一点,求占领最多点的情况下的最小字典序。
思路
先按联通块大小排序,挑选前k个,然后用优先队列去维护下一个要占领的点,注意如果联通块不够k个,那剩下来的机会就可以去占领序号较小的点,写起来意外的简单。
代码
点击查看代码
set<int>g[N];
int a[N];
int vis[N];
int ans=0;
void dfs(int x){
vis[x]=1;
ans++;
for(auto v:g[x]){
if(!vis[v]){
dfs(v);
}
}
}
bool cmp(PII x,PII y){
if(x.first!=y.first)return x.first>y.first;
return x.second<y.second;
}
void solve() {
int n,m,k;
cin>>n>>m>>k;
vector<PII>q;
for(int i=1;i<=n;i++){
g[i].clear();
a[i]=vis[i]=0;
}
for(int i=1;i<=m;i++){
int u,v;
cin>>u>>v;
g[u].insert(v);
g[v].insert(u);
}
for(int i=1;i<=n;i++){
if(!vis[i]){
ans=0;
dfs(i);
q.push_back({ans,i});
}
}
sort(q.begin(),q.end(),cmp);
priority_queue<int,vector<int>,greater<int>>p;
for(int i=0;i<k&&i<q.size();i++){
p.push(q[i].second);
}
if(k>q.size())k-=q.size();
else k=0;
int op=1;
vector<int>t;
while(p.size()){
while(p.size()&&a[p.top()])p.pop();
if(p.size()==0)break;
int x=p.top();
p.pop();
while(a[op])op++;
if(k&&op<=n&&op<x){
k--;
p.push(x);
x=op;
}
t.push_back(x);
a[x]=1;
for(auto v:g[x]){
p.push(v);
g[v].erase(x);
}
}
cout<<t.size()<<endl;
for(auto x:t){
cout<<x<<' ';
}cout<<endl;
}
K.Tokitsukaze and Shawarma
题意:
略
思路
签到
代码
点击查看代码
略
L.Tokitsukaze and XOR-Triangle
题意:
求题目里的东西。
思路
类似区间异或和,先按位处理,然后用前后缀去简化,还算是比较简单。
代码
点击查看代码
void solve() {
int q;
cin>>n>>q;
for(int i=0;i<=n;i++){
for(int j=0;j<32;j++){
a[i][j]=b[i][j]=c[i][j]=0;
}
}
for(int i=1;i<=n;i++){
int x;
cin>>x;
for(int j=0;j<32;j++){
a[i][j]=((x&(1ll<<j))>0);
a[i][j]+=a[i-1][j];
}
}
for(int i=1;i<=n;i++){
int x;
cin>>x;
for(int j=0;j<32;j++){
b[i][j]=((x&(1ll<<j))>0);
if(b[i][j]>0){
c[i][j]=(i-a[i][j]);
}else{
c[i][j]=(a[i][j]);
}
c[i][j]+=c[i-1][j];
b[i][j]+=b[i-1][j];
}
}
for(int i=1;i<=q;i++){
int l,r;
cin>>l>>r;
Z ans=0;
for(int j=0;j<32;j++){
Z s0=0,s1=0;
Z ss0=0,ss1=0;
s1+=(a[l-1][j]);
s0+=(l-1-a[l-1][j]);
ss1+=(b[r][j]-b[l-1][j]);
ss0+=(r-l+1-(b[r][j]-b[l-1][j]));
ans+=(1ll<<j)*(c[r][j]-c[l-1][j]);
ans-=(1ll<<j)*((ss0*s1+ss1*s0));
}
cout<<ans<<endl;
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!