2026 WINTER 2nd

这次补题觉得有收获的是
r5 b的问题抽象成一个最短路径 d
r6 c d
r7 d e

补题

r5

A. You Are Given Two Binary Strings...

尽量让最后几位成0

点击查看代码
void solve()
{
	string x,y;
	cin>>x>>y;
	int cnt=0,ans=0;
	for(int i=y.size()-1;i>=0;i--)
		if(y[i]=='0') cnt++;
		else break;
	for(int i=x.size()-cnt-1;i>=0;i--)
		if(x[i]=='0') ans++;
		else break; 
	cout<<ans;
}
 

B. You Are Given a Decimal String...

可抽象成模10状态图上最短路径问题
每个状态表示当前个位数字,从状态i可一步转移到(i+x)mod10或(i+y)mod10。先统计字符串中所有相邻数字对的出现次数,对每一组(x,y) 用FLoyd预处理 10 个状态之间的最短步数,若存在某对相邻字符不可达则该方案无解;否则对所有相邻对累加(最短步数−1)×出现次数,即可得到最少插入次数,从而在O(n+1000) 的时间复杂度内求出全部 10×10的答案矩阵

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e6+5;
int n;
string s;
int cnt[30][30],dis[15][15];
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	cin>>s;
	int n=s.size();
	for(int i=1;i<n;i++) 
		cnt[s[i-1]-'0'][s[i]-'0']++;
	for(int x=0;x<=9;x++,puts(""))
		for(int y=0;y<=9;y++)
		{
			memset(dis,0x3f,sizeof(dis));
			for(int i=0;i<=9;i++) 
			{
				dis[i][(i+x)%10]=1;
				dis[i][(i+y)%10]=1;
			}
			for(int k=0;k<=9;k++)
				for(int i=0;i<=9;i++)
					for(int j=0;j<=9;j++)
						dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
			long long ans=0;
			bool flag=true;
			for(int i=0;i<=9;i++)
				for(int j=0;j<=9;j++)
				{
					if(cnt[i][j]>0&&dis[i][j]==0x3f3f3f3f)
						flag=false;
					else
						ans+=(dis[i][j]-1)*cnt[i][j];
				}
			printf("%lld ",!flag?-1:ans);
		}
	return 0;
} 

D. Print a 1337-string...

https://www.luogu.com.cn/article/n7lvewoz

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

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

#define modd 998244353
const ll N = 2e5+10;
static const ll INF = (ll)4e18;


void solve() {
	ll k=300;
	ll n;cin>>n;
	ll t1=k*(k+1)/2;
	ll t=n/t1;
	ll tmp=n-t1*t;
	
	cout<<"133";
	for(ll i=1;i<=tmp;++i) cout<<'7';
	for(ll i=1;i<k;++i) cout<<'3';
	for(ll i=1;i<=t;++i) cout<<'7';
	
	
}

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;
}
这个题还是有点似懂非懂,先放着

r6

A. There Are Two Types Of Burgers

取h和c最大的,先把面包胚全分给多的,再考虑小的

B. Square Filling

这题最开始没注意审题,题目说了不要求最小化答案,按着最小化答案做的。。
如果不要求最小化的话,其实按照a数组一步一步去填b数组就好了,同时记录操作

C. Gas Pipeline

用的dp,核心思路是跟踪管道在每个位置的两种高度状态,通过状态转移逐步求解最小成本

实现的一些细节写在代码里了

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

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

#define modd 998244353
const ll N = 2e5+10;
static const ll INF = (ll)4e18;

void solve() {
	ll n,a,b;string s;
	cin>>n>>a>>b>>s;
	
	vector<vector<ll>> f(n,vector<ll>(2,INF));
	f[0][0]=a+b;
	f[0][1]=INF;//初始时(坐标0)必须在高度1,无法在2,所以设成最大值
	for(ll i=1;i<n;++i){
		if(s[i]=='1'){//必须在高度2
			f[i][0]=INF;
			f[i][1]=min(f[i-1][0]+2*a+2*b,f[i-1][1]+a+2*b);//1->2,2->2
		}else{
			f[i][0]=min(f[i-1][0]+a+b,f[i-1][1]+2*a+2*b);//0->0,1->0
			f[i][1]=min(f[i-1][0]+2*a+b,f[i-1][1]+a+2*b);//0->1,1->1
		}
	}
	cout<<f[n-1][0]+b;

}

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;
}

D. Number Of Permutations

这个还挺好想的,用容斥原理
总排列数-按第一维非递减排序的排列数-按第二维非递减排序的排列数+同时按第一维、第二维都非递减的排列数
记得加上同时的情况
主要是代码写法上差错了点。。

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

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

#define modd 998244353
const ll N = 3e5+10;
static const ll INF = (ll)4e18;

ll fact[N];

void solve() {
	ll n;cin>>n;
	vector<pair<ll,ll>>s(n);
	for(ll i=0;i<n;++i){
		cin>>s[i].first>>s[i].second;
	}
	
	fact[0]=1;
	for(ll i=1;i<=n;++i){
		fact[i]=fact[i-1]*i%modd;
	}
	
	vector<ll>cnta(n+1,0),cntb(n+1,0);
	for(auto &p:s){
		cnta[p.first]++;
		cntb[p.second]++;
	}
	
	ll a=1,b=1;
	for(ll i=1;i<=n;++i){
		a=a*fact[cnta[i]]%modd;
		b=b*fact[cntb[i]]%modd;
	}
	
	sort(s.begin(),s.end());
	
	bool ok=true;
	for(ll i=1;i<n;++i){
		if(s[i-1].second>s[i].second){
			ok=false;
			break;
		}
	}
	
	ll ab=0;
	if(ok){
		ab=1;
		ll cnt=1;
		for(ll i=1;i<n;++i){
			if(s[i]==s[i-1]) cnt++;
			else{
				ab=ab*fact[cnt]%modd;
				cnt=1;
			}
		}
		ab=ab*fact[cnt]%modd;
	}
	
	ll ans=fact[n];
	ans=(ans-a+modd)%modd;
	ans=(ans-b+modd)%modd;
	ans=(ans+ab)%modd;
	
	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;
}

r7

A. Broken Keyboard

若某字符按键故障,则每次只能输出两个相同字符,因此该字符在字符串中的所有连续段长度必为偶数;
若出现奇数长度的连续段,则不可能由故障按键产生,说明该字符对应按键一定正常。
所以只要线性扫描字符串,按连续段统计长度,将出现过奇数段的字符加入答案并按字母序输出即可

B. Binary Palindromes

问题可以转换成给定一堆0和1,以及n个目标字符串长度,求解最多可以构造多少个回文串

偶数长度串需要len/2个字符
奇数长度串需要(len-1)/2个字符对和1个单字符(若单字符不够可以拆对补充)
同时为了使构造的数量最多,应从短到长、从偶到奇

C. Minimize The Integer

由于只能交换相邻且奇偶性不同的数字,因此奇数字符之间的相对顺序不变,偶数字符之间的相对顺序也不变,只有奇偶之间可以相互穿插
于是可以将原串按奇偶拆成两个子序列 odd 和 even,它们内部顺序固定
所以 问题转化为:在保持这两个子序列内部相对顺序不变的前提下,进行一次类似归并排序的合并,每次取当前较小的数字放入答案,从而得到字典序最小的整体序列,即为可构造的最小整数。

D. Salary Changing

题目目标是不超过预算s的前提下,使最终工资序列的中位数的中位数最大

(设k=(n+1)/2,中位数=排序后的第k个数)
等价于:能否让至少k个人的工资>=x

如果某个x可行,那么所有≤x的值必然可行,所以答案关于x具有单调性,所以可以用二分答案

check核心是判断当前这种方案能否是至少k人≥x且总花费≤s
实现逻辑:
1.先发最低工资(因为任何方案都要发l)
2.统计三种人:
l≥x:直接就满足,不用额外花钱
l<x≤r:需要补(x-l)
r<x:再怎么样也无法达到x,忽略
3.判断人数是否够了
1和2走完之后,判断在所有可升级的人全拉满的情况下,是否可以凑够k个人
不能 返回false
4.贪心策略去补钱
选补钱最少的k-直接满足人数 升级

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

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

#define modd 998244353
const ll N = 2e5+10;
static const ll INF = (ll)4e18;

bool check(ll x,const vector<pair<ll,ll>>&a,ll s,ll n){
	ll k=(n+1)/2;
	
	ll cost =0;
	ll cnt=0;
	vector<ll>need;
	
	for(auto &[l,r]:a){
		cost+=l;
		if(l>=x) cnt++;
		else if(r>=x) need.emplace_back(x-l);
	}
	
	if(cnt+(ll)need.size()<k) return false;
	
	sort(need.begin(),need.end());
	
	for(ll i=0;i<k-cnt;++i){
		cost+=need[i];
	}
	
	return cost<=s;
}

void solve() {
	ll n,s;cin>>n>>s;
	
	vector<pair<ll,ll>>a(n);
	for(ll i=0;i<n;++i){
		cin>>a[i].first>>a[i].second;
	}
	
	ll L=1,R=1e18,ans=1;
	while(L<=R){
		ll mid=(L+R)>>1;
		if(check(mid,a,s,n)){
			ans=mid;
			L=mid+1;
		}else{
			R=mid-1;
		}
	}
	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;
}

E1. Voting (Easy Version)

我觉得这题赛时能出的
没看到这题,放后面了。。
每个选民有两种方式被说服 花钱买票和群众压力
所以贪心策略先用最少的前去买一些选民,逐步扩大支持人数,触发连锁反应,最终覆盖所有人

代码实现:
始终维护当前支持“我”的人cur
把所有选民mi从小到大排序,用一个小根堆存储当前可购买的人的pi

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

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

#define modd 998244353
const ll N = 1e6+10;
static const ll INF = (ll)4e18;

ll fact[N];
struct node{
	ll m,p;
}a[N];
priority_queue<ll,vector<ll>,greater<ll>>q;
bool cmp(node a,node b){
	if(a.m==b.m) return a.p<b.p;
	return a.m<b.m;
}

void solve() {
	ll n;cin>>n;
	ll ans=0;
	while(!q.empty()){
		q.pop();
	}
	
	for(ll i=1;i<=n;++i) cin>>a[i].m>>a[i].p;
	
	sort(a+1,a+1+n,cmp);
	for(ll i=n;i>=1;i--){
		q.push(a[i].p);
		if(a[i].m>n-(ll)q.size()){
			ans+=q.top();
			q.pop();
		}
	}
	
	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;
}
posted @ 2026-01-31 03:58  YuanqLi  阅读(7)  评论(0)    收藏  举报