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)

题意:

定义二元组对应的值f(i,j)=(ai+aj)modp,求全部不同的二元组中的前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;
		
	}
}
posted @   ptlks  阅读(11)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示