AtCoder Beginner Contest 260 (D-E)

AtCoder Beginner Contest 260 - AtCoder

D - Draw Your Cards

题意:N张卡牌数字 1-n,以某种顺序排放,每次拿一张,如果这一张比前面某一张小(不是最大的) ,就把它放在比他大的牌中数字最小的,如果没有就单独放在第一个位置,当某个位置的牌数达到k张,就将他们拿走,问最后每张牌都会在第几次操作被拿走,如果没被拿走输出 -1。

题解:可以利用set或者map这种自动排序的迭代器来进行存储,然后利用二分找到第一个比它大的数字,然后进行操作即可。

其中: set的速度可能会比map慢

 set写法 

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> pll;
const int N=2e5+5;
const ll inf=1e18;
const ll mod=998244353;
ll n,k;
ll a[N],b[N],pre[N];
set<ll> q;
ll sum[N],ans[N];
signed main(){
   ios::sync_with_stdio(false);
   cin.tie(0);cout.tie(0);
   cin>>n>>k;
   for(ll i=1;i<=n;i++) pre[i]=-1;
   for(ll i=1;i<=n;i++){
      ll t;cin>>t;
      if(k==1){
         ans[t]=i;continue;//k==1特判一下
      }
      sum[t]=1;
      q.insert(t);    
      if(*(q.rbegin())!=t){    
         ll pt=*(upper_bound(q.begin(),q.end(),t));//二分找到第一个比他大的
         pre[t]=pt;
         q.erase(pt);//删除掉,让小的覆盖它
         sum[t]+=sum[pt];
         if(sum[t]==k){
            ll beg=t;
            while(beg!=-1){
               ans[beg]=i;
               beg=pre[beg];
            }
            q.erase(t);//如果能凑出来,就要全部删掉
         }
      }
   }
   for(ll i=1;i<=n;i++) cout<<(ans[i]?ans[i]:-1)<<endl;

}

 

map写法 

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> pll;
const int N=2e5+5;
const ll inf=1e18;
const ll mod=998244353;
ll n,k;
ll a[N],b[N],pre[N];
map<ll,ll> q;
ll sum[N],ans[N];
signed main(){
   ios::sync_with_stdio(false);
   cin.tie(0);cout.tie(0);
   cin>>n>>k;
   for(ll i=1;i<=n;i++) pre[i]=-1;
   for(ll i=1;i<=n;i++){
      ll t;cin>>t;
      if(k==1){
         ans[t]=i;continue;
      }
      sum[t]=1;
      q[t]=1;
      if(q.rbegin()->first!=t){    
         auto pt=q.upper_bound(t);    
         pre[t]=pt->first;
         q.erase(pt);
         sum[t]+=sum[pt->first];
         if(sum[t]==k){
            ll beg=t;
            while(beg!=-1){
               ans[beg]=i;
               beg=pre[beg];
            }
            q.erase(t);
         }
      }
   }
   for(ll i=1;i<=n;i++) cout<<(ans[i]?ans[i]:-1)<<endl;
}

 

E - At Least One

题意: 给出n对数字,没对数字最少选择其中的一个,找出所有选择的数字都在某个区间内的区间数目

题解: 加入对于每个数  l <= xi <=r,那么对于这个区间肯定满足,那么,对与一个区间 [a,b] (a<=l, b>=r) 肯定也满足,因为肯定也在其中,所以,我们只需要找到某个最小的区间,然后向两边延申即可,其中,更新每一种长度的值可以用差分,为了防止重复,我们可以固定右端点,跑左端点。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> pll;
const int N=2e5+5;
const ll inf=1e18;
const ll mod=998244353;
ll n,k;
ll a[N],b[N],pre[N];
ll q[N],sum[N];
vector<ll> p[N];
map<ll,ll> mp;
bool check(ll x){
   for(auto it:p[x]){
      if(mp[it]<=1) return 0;//如果其中某个位置的出现的次数<=1就不能删
   }
   return 1;
}
signed main(){
   ios::sync_with_stdio(false);
   cin.tie(0);cout.tie(0);
   cin>>n>>k;
   for(ll i=1;i<=n;i++){
      ll x,y;cin>>x>>y;
      p[x].push_back(i);//存储每个数字的位置
      p[y].push_back(i);
   }
   ll l=0,r=-1;//左右两个端点
   ll le=0;//用来记录遍历过第一对点
   for(ll i=1;i<=k;i++){
       q[++r]=i;
       for(auto it:p[i]){
         mp[it]++;//记录第几队点出现的次数
         if(mp[it]==1) le++; 
       }
       while(l<=r&&check(q[l])){
         for(auto it:p[q[l]]) mp[it]--; //判断左端点的这个点,它可不可以删
         l++;
       }
       if(le==n) {sum[r-l+1]++;sum[i+1]--;} 
   }
   for(ll i=1;i<=k;i++) sum[i]+=sum[i-1],cout<<sum[i]<<" ";//差分遍历
}

 

 

posted @ 2022-08-04 10:33  HHzp  阅读(43)  评论(0)    收藏  举报