2026 WINTER 3rd

有收获的是:
cf r8d r9d
牛客 训1g 训2h 训3 cf
以及训1 cl的瞎了眼的教训。。。

补题

cf r8

A. Prime Subtraction

计算x和y差值,若差大于1行,否则不行。
因为在差值大于1的情况下,若是合数,能拆成质数减;若是质数,则可以直接减去

B. Kill 'Em All

从最右侧的怪兽开始处理,依次把左边的怪兽左移

C. Standard Free2play

只关注初始伸出的平台序列,通过判断 “相邻伸出平台的高度差” 决定是否需要水晶,差 > 1 则必须用 1 颗,否则无需

D. AB-string

参考https://www.luogu.com.cn/article/vbj3hnn7
把求好串转换成[n*(n-1)/2-cnt]-cnt,cnt为badstring个数
只有四种badstring

  1. ABBBB...
  2. BAAAA...
  3. ...AAAAB
  4. ...BBBBA
    即 统计这四种串的个数
点击查看代码
#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define endl '\n'
const ll modd=998244353;

const ll N=5e5+10;
ll a[N];

ll cnt;

void solve()
{
	ll n;cin>>n;
	string s;cin>>s;
	for(ll i=0;i<n;++i){
		a[cnt]=1;
		while(i!=n&&s[i]==s[i+1]){
			a[cnt]++;
			i++;
		}
		cnt++;
	}
	
	cout<<(n*(n-1))/2-2*n+a[0]+a[cnt-1]+cnt-1;
	//badstring个数等于单个字符作为bad核心的扩展-能构成回文的情况(左端修复和右端修复)-重复计算的(边界)
}

int main()
{
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	
	ll lll=1;
//	cin>>lll;
	while(lll--)
	{
		solve();
//		if(lll) cout<<'\n';
	}
	return 0;
}

cf r9

A. Two Rival Students

最大的距离是min(n-1,初始距离+x)

B. Magic Stick

如果x大于等于y,输出YES(一直减一)
否则,若x>3,可以通过操作2变大,就变成x>=y的情况
若x为1 2 3,特判就好了

C. Dominated Subarray

双指针找到满足条件的最短子数组

D. Yet Another Monster Killing Problem

预处理,快速找到 打k只怪兽时,能用到的最强英雄
再模拟一遍,从当前位置开始,每天尽可能打最多的怪兽

点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int maxn=2e5+9;
int a[maxn],power[maxn],day[maxn],better[maxn],n,m;
int main()
{
	int t;
	cin>>t;
	while(t--)
	{
		int flag=1;
		cin>>n;
		for(int i=1;i<=n;i++)	cin>>a[i];
		cin>>m;
		for(int i=0;i<=n;i++)	better[i]=0;
		for(int i=1;i<=m;i++)
		{
			cin>>power[i]>>day[i];
			better[day[i]]=max(better[day[i]],power[i]);
		}
		for(int i=n-1;i>=1;i--)
			better[i]=max(better[i],better[i+1]);
		int ans=0,pos=1;
		while(pos<=n)
		{
			ans++;
			int maxx=0,temp=pos;
			while(1)
			{
				maxx=max(maxx,a[temp]);
				if(better[temp-pos+1]<maxx)	break;
				temp++;
			}
			if(temp==pos)	{flag=0;break;}
			pos=temp;
		}
		if(flag==0)	cout<<-1<<endl;
		else cout<<ans<<endl;
	}
}

牛客1

https://ac.nowcoder.com/acm/contest/120561#question
好久没打牛客了,上手就wa了好几发。。。。
但是之后调好了状态,写起来还行
(但感觉自己写的那几题都算简单的

然后还被榜给带过去看A,看好一会 不会。。。

C

没看到开区间,直接求了个最大值交上去。。
正解应该是a[1]+a[n]+(n-2)*maxx

L

也是想当然了,没看到“最小的”,眼睛长了干嘛的。。

E

其实就是在一个序列里面比相邻两个数的和的最大值

K

手算几下会发现 只有1 3可以
其他的都不行
1是“1”,3是“1 2 3”

G

其实没怎么做过这种题。。。

不直接找最大折叠数字,而是在区间构造一个数,使它反转后尽可能的大

点击查看代码
#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define endl '\n'
const ll modd=998244353;

ll rev(ll x)
{
    string s=to_string(x);
    reverse(s.begin(),s.end());
    return stoll(s);
}

void solve()
{
    ll l,r;cin>>l>>r;
    string t=to_string(r);

    if(l==r){
        reverse(t.begin(),t.end());
        cout<<stoll(t)<<endl;
        return;
    }

    if(t[0]=='1'){
        bool ok=true;
        for(int i=1;i<(int)t.size();i++)
            if(t[i]!='0') ok=false;
        if(ok){
            cout<<r-1<<endl;
            return;
        }
    }

    //起点
    ll ans=1;
    for(int i=1;i<(int)t.size();i++) ans*=10;
    ans=max(ans,l);

    string s=string(18,'0')+to_string(ans);
	//从低位开始填9
    for(ll i=1,j=s.size()-1;i<=1e16;i*=10,j--){
        for(int k=1;k<='9'-s[j];k++){
            if(ans+i<=r) ans+=i;
        }
    }

	//翻转输出
    cout<<rev(ans)<<endl;
}

int main()
{
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    ll lll=1;
    cin>>lll;
    while(lll--)
    {
        solve();
    }
    return 0;
}

牛客2

https://ac.nowcoder.com/acm/contest/120562#question
只来得及补H...抽空继续补吧。。

H

题意:定义一个数组的权值为其所有前缀中不同数字的个数,求所有子数组的权值之和

看题解说正难则反
考虑一个数字能给多少个子数组贡献答案以及贡献的大小。
在算ai时,考虑的子数组必然是包含i这个位置的,所以计算过程只会被上一个ai的位置j影响,影响的区间左端点个数为i−j 个,而以l(j < l ≤ i) 为左端点,r(i ≤r ≤n) 为右端点的区间贡献(i−j)×(1+n−i+1)× (n−i+1)/2次

点击查看代码
#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define endl '\n'
const ll modd=998244353;

const ll N=5e5+10;
ll a[N];

ll cnt;

void solve()
{
	ll n;cin>>n;
	map<ll,vector<ll>>mp;
	for(ll i=1;i<=n;++i){
		ll x;cin>>x;
		mp[x].push_back(i);
	}
	auto ans=0ll;
	for(auto &[x,y]:mp){
		y.insert(y.begin(),0);
		for(ll i=1;i<(ll)y.size();++i){
			ll l=y[i-1];
			ll r=n-y[i]+1;
			ans+=1ll*r*(r+1)/2*(y[i]-l);
		}
	}
	cout<<ans;
}

int main()
{
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	
	ll lll=1;
	cin>>lll;
	while(lll--)
	{
		solve();
//		if(lll) cout<<'\n';
	}
	return 0;
}

牛客3

https://ac.nowcoder.com/acm/contest/120563#question
赛时通过5题,补题补了下c和f

(按赛时开的顺序写)

A

因为x范围很小 只到100,所以从1到10枚举k即可

G

设左边砝码重量总值为suma,右边为sumb
求出两边差值,
若为0,拿掉一个就好
否则,如果左边>右边,从大的往小的拿,一直到拿掉的重量综合大于等于差值
右边大的情况也同理

B

分解质因子,用哈希表记录首次出现位置,一旦某个质因子重复出现,立即得到一组gcd>1的数

ps:为啥赛后看好几个过的是直接枚举过的,不是n方吗,会T不是。。

J

预处理最后一层的深度和节点数,然后对每个查询:
如果查询节点不在最后一层,直接返回2^深度
如果在最后一层返回预处理好的节点数

H

三角形面积公式
image

c=xa​yb​−xb​ya
d=ya​−yb​

∣c+d⋅x∣=4

C

这个思考方式我觉得我得学学
目标字符串只可能是 0101... 或 1010...,分别计算最少操作次数,取较小值。
对于固定目标,抽取原串中已经在正确位置上的字符形成串 t,把 '1' 记为 +1,'0' 记为 -1。
一次操作最多只能将某一段的“优势差”减少 1,因此最少操作次数等于 t 的最大子段和与最小子段和绝对值中的较大者,即max(max_subarray_sum, |min_subarray_sum|)。
最终对两种目标取最小值输出。

点击查看代码
#include <bits/stdc++.h>
using namespace std;

#define ll long long
#define endl '\n'

ll f(const string &s)
{
    ll mx = 0, mn = 0, now = 0;

    for (int i = 0; i < (int)s.size(); ++i) {
        if (s[i] == '1') now++;
        else now--;
        if (now < 0) now = 0;
        mx = max(mx, now);
    }

    now = 0;
    for (int i = 0; i < (int)s.size(); ++i) {
        if (s[i] == '1') now++;
        else now--;
        if (now > 0) now = 0;
        mn = min(mn, now);
    }

    return max(mx, llabs(mn));
}

void solve()
{
    ll n;
    string s, t;
    cin >> n >> s;

    t.clear();
    for (int i = 0; i < n; ++i) {
        if ((i & 1) && s[i] == '1') t.push_back(s[i]);
        if (!(i & 1) && s[i] == '0') t.push_back(s[i]);
    }
    ll ans = f(t);

    t.clear();
    for (int i = 0; i < n; ++i) {
        if ((i & 1) && s[i] == '0') t.push_back(s[i]);
        if (!(i & 1) && s[i] == '1') t.push_back(s[i]);
    }
    ans = min(ans, f(t));

    cout << ans << endl;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int T;
    cin >> T;
    while (T--) solve();

    return 0;
}

F

感觉自己手动推能推出来点,但是逻辑不完整

把博弈过程看成“不断删点但保持连通”,则游戏结束时剩余格子必然构成一条从 (1,1) 到第n列的唯一通路。在2×n 网格中,最短路长度等于(n−1)+r,其中r为换行次数。关键在于:要强制产生一次换行,最少需要连续 5 列结构,因此小紫每 5 列最多逼出一次换行,而小红也能把换行次数控制在每 5 列至多一次。故最终r=⌊n/5⌋,答案为(n−1)+⌊n/5⌋

posted @ 2026-02-08 01:49  YuanqLi  阅读(3)  评论(0)    收藏  举报