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个空要插入大于小于号,然后得平均分,现在平均不了了,无解
如果数字有重复,那就直接把重复的绑在一起 ,如果构造不了就考虑拆开
*/

浙公网安备 33010602011771号