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]<<" ";//差分遍历 }