cf1353D 1600的题
这题先说一下题意吧,就是给你一个n让你对这个长度为n且全为0的数组进行操作,首先是从这个数组中寻找含有0最多的一个区间,左右端点分别为l,r,如果r-l+1能整除2,那么这个数组a[(l+r-1)/2]=i,否则a[(l+r)/2] = i,这里的i指的是操作的次数,比如第一次进行操作的数相对来说是这个数组中间的位置,那么这个操作数就为1,之后进行递增。
首先可以看一下基本的模拟,肯定TLE的模拟
一个结构体表示该区间的0的个数ans和左端点l和右端点r,然后对其进行排序,之后ans最大的node里的l和r即为新的l和r,复杂度为O(n^2)。应该是挺好理解的
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
using namespace std;
typedef long long ll;
#define rep(i, a, n) for(int i = a; i < n; i++)
#define per(i, a, n) for(int i = n-1; i >= a; i--)
#define INF 1ll<<60
const int maxn = 2e5+10;
struct node //结构体用来存储左右端点和0的个数,方便之后的排序
{
int ans, l, r;
bool operator < (const node &x){
if(x.ans==ans){
return l<x.l;
}else
{
return ans>x.ans;
}
}
};
int a[maxn];
int main(){
ios::sync_with_stdio(0);
int t;
cin>>t;
while(t--){
memset(a,0,sizeof(a));
int x;cin>>x;
int l = 1, r = x;
for(int i = 1; i <= x; i++){
if((r-l+1)%2==1){ //进行修改的点
a[(l+r)/2] = i;
}else a[(l+r-1)/2] = i;
node b[x+10];
int k = 0;
for(int j = 1; j <= x; j++){
if(a[j]==0) b[k].l = j; //左端点
else continue;
while(a[j]==0&&j<=x) j++; //对0进行一个一个的遍历,最傻的方法。。。
b[k].r = j-1; //右端点
b[k].ans = b[k].r-b[k].l+1;
k++;
}
sort(b,b+k);
l = b[0].l;r=b[0].r;
}
for(int i = 1; i <= x; i++){
printf("%d ", a[i]);
}
printf("\n");
}
return 0;
}
然后来一个进阶的方法,用vector进行包装,而且很显然的是不能像第一个模拟一样一个一个的遍历过去,所以这里用了区间的方式,对vector进行插入,但还是在2e5的时候TLE了
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
using namespace std;
typedef long long ll;
#define rep(i, a, n) for(int i = a; i < n; i++)
#define per(i, a, n) for(int i = n-1; i >= a; i--)
#define INF 1ll<<60
const int maxn = 2e5+10;
struct node
{
int ans, l, r;
bool operator < (const node &x){
if(x.ans==ans){
return l<x.l;
}else
{
return ans>x.ans;
}
}
};
int a[maxn];
int main(){
ios::sync_with_stdio(0);
int t;
cin>>t;
while(t--){
memset(a,0,sizeof(a));
int x;cin>>x;
int l = 1, r = x;
vector<node> v;
for(int i = 1; i <= x; i++){
int tmp; //进行修改的点
if((r-l+1)%2==1){
tmp = (l+r)/2;
a[tmp] = i;
}else tmp = (l+r-1)/2, a[tmp] = i;
//当该点修改了之后,原本的区间就被拆分为了两个区间,把这两个区间放进vector中,即可得到这个点修改之后得到的结果
//然后再对vector进行一遍排序之后便可得到含有0最多的区间,并且要注意把这个为0的区间删去
v.push_back(node{tmp-l+1,l,tmp-1});
v.push_back(node{r-tmp+1,tmp+1,r});
sort(v.begin(),v.end());
l = v[0].l;r=v[0].r;
v.erase(v.begin(), v.begin()+1);
}
for(int i = 1; i <= x; i++){
printf("%d ", a[i]);
}
printf("\n");
}
return 0;
}
最后一步的set大法,为什么会从vector到set呢,因为set和map的插入和删除都比其他的容器相对来说会快一点,而且事实证明也的确如此,而且set还会进行自动排序,最多是set里不能有重复的元素,但在这一题,显而易见的是不可能有相同的元素,所以也就不会漏掉,起码这题在2e5的地方过了,而且还是以300多ms过的,个人感觉还行,但应该不是最好的方法,后面还会再进行补充。
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
#include <set>
using namespace std;
typedef long long ll;
#define rep(i, a, n) for(int i = a; i < n; i++)
#define per(i, a, n) for(int i = n-1; i >= a; i--)
#define INF 1ll<<60
const int maxn = 2e5+10;
struct node
{
int ans, l, r;
bool operator < (const node x) const{
if(x.ans==ans){
return l<x.l;
}else
{
return ans>x.ans;
}
}
};
int a[maxn];
int main(){
ios::sync_with_stdio(0);
int t;
cin>>t;
while(t--){
memset(a,0,sizeof(a));
int x;cin>>x;
int l = 1, r = x;
set<node> v;
for(int i = 1; i <= x; i++){
int tmp;
if((r-l+1)%2==1){
tmp = (l+r)/2;
}else tmp = (l+r-1)/2;
a[tmp] = i;
//这里的插入和删除和vector是一样的思路,但是比vector快
v.insert(node{tmp-l+1,l,tmp-1});
v.insert(node{r-tmp+1,tmp+1,r});
set<node>::iterator it;
it = v.begin();
l = (*it).l;r=(*it).r;
v.erase(v.begin());
}
for(int i = 1; i <= x; i++){
printf("%d ", a[i]);
}
printf("\n");
}
return 0;
}