D
- 题意: 一个序列a,对每个a[i]会循环向后走,直到找到第一个(a[j\%n]<frac{a[i]}{2}),问每个a[i]最多走几步.
- 思路: 把a重复三遍(每个a[i]最多走2*n,否则为-1),对于a[i]被卡有两种情况,要么他后面有(a[j\%n]<frac{a[i]}{2}),要么是a[j]>a[i],这样a[j]更容易比a[i]卡,具体怎么维护看注释吧
#include<bits/stdc++.h>
#define ll long long
#define pii pair<int,int>
using namespace std;
int main(){
int n; cin >> n;
vector<int> a(3*n);
for(int i=0;i<n;++i){
cin >> a[i]; a[i+n] = a[i+n+n] = a[i];
}
vector<int> ans(3*n);
vector<int> st_max,st_min;
for(int i=3*n-1;i>=0;--i){
while(st_max.size() && a[st_max.back()] < a[i]) st_max.pop_back(); // 单调减
while(st_min.size() && a[st_min.back()] > a[i]) st_min.pop_back(); // 单调增
int low = 0, high = st_min.size(); // 二分找卡 a[i]的点
while(low < high){
int mid = (low + high) >> 1;
if(a[st_min[mid]]*2 <a[i]) // 卡a的点都在单调增栈中
low = mid+1;
else high = mid;
}
int nxt = 3*n; // 最后一个 相当于没找到
if(low > 0) nxt = min(nxt,st_min[low-1]); // 找得到更新nxt
if(!st_max.empty()) nxt = min(nxt,st_max.back()); // 单调减栈中的值肯定比a[i]大,且在i后面,比a[i]更易被卡
if(nxt < 3*n && a[nxt] >= a[i]) ans[i] = ans[nxt]; // nxt 被卡
else ans[i] = nxt; // i被nxt卡
st_min.push_back(i);
st_max.push_back(i);
}
for(int i=0;i<n;++i){
if(i>0) cout << ' ';
cout << (ans[i]==3*n?-1:ans[i]-i) ;
}
cout << '
';
return 0;
}
- 可以用单调增维护单调性,然后在单调栈中二分.
- 因为后面值会影响前面,所以从后向前统计.
C
- 题意: 三维空间的一些点,每次选择两个未被删除的点删除且不能有其他点在两个点组成的矩形内部或边界.求一个删除方案
- 思路: 直接排序,第一次删除前两维都相同的点对,第二次删除第一维相同的点对,第三次删除排序后位置相邻的点对.
#include<bits/stdc++.h>
#define ll long long
#define pii pair<int,int>
using namespace std;
const int N = 5e5+10;
struct node{
int x,y,z,id;
}a[N];
int n,vis[N];
bool cmp(node p1,node p2){
if(p1.x!=p2.x) return p1.x < p2.x;
if(p1.y!=p2.y) return p1.y < p2.y;
return p1.z < p2.z;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i){
scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);
a[i].id = i;
}
sort(a+1,a+1+n,cmp);
for(int i=1;i<=n-1;++i){
if(a[i+1].x == a[i].x && a[i+1].y == a[i].y){
vis[i] = vis[i+1] = 1;
printf("%d %d
",a[i].id,a[i+1].id);
i++;
}
}
for(int i=1;i<=n-1;++i){
if(vis[i]) continue;
int j = i+1;
while(vis[j]) j++;
if(a[i].x == a[j].x){
vis[i] = vis[j] = 1;
printf("%d %d
",a[i].id,a[j].id);
i = j; // i到j全部被访问过, 直接跳避免n^2复杂度
}
}
for(int i=1;i<=n-1;++i){
if(vis[i]) continue;
int j = i+1;
while(vis[j]) j++;
vis[i] = vis[j] = 1;
printf("%d %d
",a[i].id,a[j].id);
i = j;
}
return 0;
}
B
- 题意: 一排车,有初始的位置和最终的位置,问有多少车超车了
- 思路: 从后向前推,维护初始位置最靠前的车,当前位置的车的初始位置比维护的那辆车靠后,则当前这辆车就超车了.
#include<bits/stdc++.h>
#define ll long long
#define pii pair<int,int>
using namespace std;
const int N = 1e5+10;
int a[N],n,pos[N],b[N],c[N];
int main(){
ios::sync_with_stdio(false);
cin >> n;
for(int i=1;i<=n;++i) {
cin >> a[i] , pos[a[i]] = i;
}
int val,cnt = 0,ans = 0,dif;
for(int i=1;i<=n;++i){
cin >> val;
b[i] = pos[val];
}
c[n] = b[n];
for(int i=n-1;i>=1;--i){
c[i] = min(c[i+1],b[i]);
}
for(int i=1;i<n;++i){
if(b[i]>c[i+1]) ans++;
}
cout << ans << '
';
return 0;
}
tourist爷随便出个智力题就把我卡死了,QAQ希望能为区域赛攒人品吧