2026.4.18

https://htoj.com.cn/cpp/oj/contest/detail/problem?cid=22735850703872

得分:\(100+100+100+20+0+30=350\),T5 博弈论直接一点不会 /tuu

A 搞半天还得自己拼

送分题,直接递增排序即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
constexpr int N=500*500+2;
int T,n,m;
ll a[N],ans;
inline ll solve()
{
	ans=0;
	cin>>n>>m;
	for(int i=1;i<=n*m;i++) cin>>a[i];
	sort(a+1,a+n*m+1);
	for(int i=1;i<=n*m;i+=m) ans+=a[i];
	return ans;
}
int main()
{
//	freopen("neuvillette.in","r",stdin);
//	freopen("neuvillette.out","w",stdout);
	cin.tie(0)->sync_with_stdio(0);
	cin>>T;
	while(T--) cout<<solve()<<'\n';
	cout.flush();
	return 0;
}

B 涂涂改改

直接把所有的位置扫一遍,然后每一个位置都二分一下半径,然后扫一遍。

发现如果直接这么干 check 的时间复杂度直接爆炸了,所以考虑优化:维护连续的 1 作为连通块,假如修改 \([l,r]\) 那么就在 \(l-1,r+1\) 探测一下还有没有连续的 1,如果有就扩展长度,时间复杂度 \(O(n \log^2 n)\)。但是两只 \(\log\) 在本机跑了 \(2\operatorname{s}\) 多,所以就把一只 \(\log\) 拿出来丢到外面 \(O(n \log n)\) 预处理,这样子就是 \(O(n \log n)\) 了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
constexpr int N=1e5+7;
int n,k;
bool a[N];
struct node{int l,r;};
vector<node> v;   //记录1连通块 
node lr[N];  //i所在连通块左右端点 
inline node get(int x)   //x所在连通块左右端点 
{
	if(x<0||x>n) return {-1,-1};
	int l=0,r=v.size()-1;
	while(l<=r)
	{
		int mid=(l+r)>>1;
		if(v[mid].l<=x&&x<=v[mid].r) return v[mid];
		else if(v[mid].r>x) r=mid-1;
		else l=mid+1;
	}
	return {-1,-1};
}
inline bool check(int pos,int R)
{
	int l=max(1,pos-R),r=min(n,pos+R);
	//扩展左边界 
	node lv=lr[l-1];
	node rv=lr[r+1];
	if(lv.l!=-1) l=lv.l; if(rv.r!=-1) r=rv.r;
//	cerr<<'\t'<<l<<' '<<r<<'\n';
	if(r-l+1>=k) return 1;
	return 0;
}
inline int calc(int pos)
{
	int l=-1,r=n;
	while(l+1<r)
	{
		int mid=l+r>>1;
		if(check(pos,mid)) r=mid;
		else l=mid;
	}
	return r;
}
inline int solve()
{
	cin>>n>>k; v.clear();
	string tmp; cin>>tmp;
	for(int i=1;i<=n;i++) a[i]=tmp[i-1]-'0';
	int li=1,ri=1;
	for(int i=2;i<=n;i++)
	{
		if(!a[i]&&a[i-1]) v.push_back({li,ri});    //1110
		if(a[i]&&!a[i-1]) li=ri=i;  //0001
		if(a[i]) ri=i;
	}
	for(auto vv:v) if(vv.r-vv.l+1>=k) return 0;
	lr[0]=lr[n+1]={-1,-1};
	for(int i=1;i<=n;i++) lr[i]=get(i);
//	for(auto vv:v) cerr<<vv.l<<' '<<vv.r<<'\n';
	int ans=n;
	for(int i=1;i<=n;i++)
	{
		int res=calc(i);
		ans=min(ans,res);
//		cerr<<i<<' '<<res<<'\n';
	}
	return ans;
}
int main()
{
//	freopen("neuvillette.in","r",stdin);
//	freopen("neuvillette.out","w",stdout);
	cin.tie(0)->sync_with_stdio(0);
	int T;
	cin>>T;
	while(T--) cout<<solve()<<'\n';
	cout.flush();
	return 0;
}
/*
O(logn)二分半径,O(n)选位置,O(log) check?
选择[l,r]修改,l-1,r+1探测一下还有没有连续的1 
*/

C 对称美学

答案在说什么我看不懂,感觉赛时这种做法还是比较清晰的 虽然代码比较难写

题意转化为把 \(a\) 重排,使得 \(cnt(a[i]<a[i+1])=cnt(a[i]>a[i+1])\)

如果加一个数字不重复的限制似乎会更好做一点,就直接分讨一下

  • 如果 \(n\) 为奇数:按照 最小-小-最大-小-最小 的顺序排序
  • 如果 \(n\) 为偶数:一共有 \(n-1\) 个空要插入大于小于号,然后得平均分,现在平均不了了,无解

如果数字有重复,那就直接把重复的绑在一起 ,如果构造不了就考虑拆开。先拆不是最大值的那个数,然后就丢到另外一边去就好了。但是如果只能拆最大值,那就只能把最大值拆掉,丢到数少的那一边的最左边/最右边就好了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
constexpr int N=1e5+7;
int n;
vector<int> a,b;
vector<int> b1,b2;  //平均分成左右两段,长度(m-1)/2 
unordered_map<int,int> cnt;  //重复数字计数 
inline void print(int nn,int x)
{
	while(x--) cout<<nn<<' ';
}
int main()
{
//	freopen("neuvillette.in","r",stdin);
//	freopen("neuvillette.out","w",stdout);
	cin.tie(0)->sync_with_stdio(0);
	cin>>n;
	for(int i=0,x;i<n;i++) {cin>>x; a.push_back(x);};
	for(int i=0;i<n;i++) cnt[a[i]]++;
	sort(a.begin(),a.end());
	b=a; b.erase(unique(b.begin(),b.end()),b.end());
//	for(auto c:b) cerr<<c<<' '; cerr<<'\n';
	int m=b.size();
	if(m%2==1)
	{
		int mx=*max_element(b.begin(),b.end());
		for(int x:b)
		{
			if(x==mx) continue;
			if(b1.size()<(m-1)/2) b1.push_back(x);
			else b2.push_back(x);
		}
		sort(b1.begin(),b1.end());
		sort(b2.begin(),b2.end(),greater<int>());
		for(auto c:b1) print(c,cnt[c]);
		print(mx,cnt[mx]);
		for(auto c:b2) print(c,cnt[c]);
	}
	else   //考虑拆开一个重复的 
	{
		int mx=*max_element(b.begin(),b.end());
		int reduce=-1;  //要拆开的数,在实在没有办法的方法下才拆mx
		//考虑把拆开的这个数先扔掉,然后就是平均了,是不是还要特判max? 
		for(auto ff:cnt) if(ff.second>1)
		{
			reduce=ff.first;
			break;
		}
		if(reduce==-1)
		{
			cout<<"-1";
			exit(0);
		}
		bool neuvillette=0;
		if(reduce==mx) neuvillette=1; //拆掉的是max,要特判
		for(int x:b)
		{
			if(x==mx||x==reduce) continue;
			if(b1.size()<(m-1)/2) b1.push_back(x);
			else b2.push_back(x);
		}
		if(!neuvillette)
		{
			b1.push_back(reduce); b2.push_back(reduce);
			sort(b1.begin(),b1.end());
			sort(b2.begin(),b2.end(),greater<int>());
			for(auto c:b1)
			{
				if(c==reduce) print(c,cnt[c]-1);
				else print(c,cnt[c]);
			}
			print(mx,cnt[mx]);
			for(auto c:b2)
			{
				if(c==reduce) print(c,1);
				else print(c,cnt[c]);
			}
		}
		else
		{
			sort(b1.begin(),b1.end());
			sort(b2.begin(),b2.end(),greater<int>());
			for(auto c:b1) print(c,cnt[c]);
			print(mx,cnt[mx]-1);
			for(auto c:b2) print(c,cnt[c]);
			print(mx,1);
		}
	}
	cout.flush();
	return 0;
}
/*
题意:把a重排,使得cnt(a[i]<a[i+1])=cnt(a[i]>a[i+1])
如果保证数字不重复: 
方案:n为奇数 最小 小 最大 小 最小 
	  n为偶数 一共有n-1个空要插入大于小于号,然后得平均分,现在平均不了了,无解 
如果数字有重复,那就直接把重复的绑在一起 ,如果构造不了就考虑拆开 
*/

D 菱形计数

posted @ 2026-04-23 16:50  wwwidk1234  阅读(36)  评论(0)    收藏  举报