Codeforces Round #546 (Div. 2)
题目链接:https://codeforces.com/contest/1136
A. Nastya Is Reading a Book
题意:
一本书有n个章节,之后给出每个章节所在页数范围,然后有一个人来看书看了k页,问还有多少章节没看。
题解:
水题。模拟一下就好了。
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 2e5+5; int n; int l[N],r[N]; int main(){ ios::sync_with_stdio(false);cin.tie(0); cin>>n; for(int i=1;i<=n;i++) cin>>l[i]>>r[i]; int k; cin>>k; int p = lower_bound(l+1,l+n+1,k)-l; if(l[p]==k) cout<<n-p+1; else cout<<n-p+2; return 0; }
B. Nastya Is Playing Computer Games
题意:
有n个格子,每个格子一开始都有一个硬币,硬币上面都有一个石头。现在有一个人在k这个位置,问他需要多少次操作能将所有的硬币捡完。操作可以分为以下三类:
1.左右移动一格;2.将当前硬币上面的石头扔向另外一个格子(可以自己选择);3.捡起当前的硬币。
题解:
思路就是将操作分开看,移动所需要的花费以及捡硬币所需要的花费。这里扔石头是有贪心思想的,也就是说我们尽可能地将石头扔向那些没有硬币的格子中。
具体看代码吧:
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 2e5+5; int n,k; int main(){ ios::sync_with_stdio(false);cin.tie(0); cin>>n>>k; cout<<n + n+1 + n-1+min(n-k,k-1); //捡硬币,扔石头,和移动 return 0; }
C. Nastya Is Transposing Matrices
题意:
给出两个矩阵,现在你可以对第一个矩阵的任意一个子矩阵进行转置操作,即将行列对换,可以进行多次操作。最后问是否能够将第一个矩阵变为第二个矩阵。
题解:
观察转置操作,其实就是将一条对角线上面的数进行了重排列。那么我们就可以将一个较大矩阵的操作,变为许多2*2矩阵的转置操作(改变对角线)。
所以我们只需要判断一下两个矩阵对角线上面的值是否一样就行了。
代码如下:
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 505; int n,m; int a[N][N]; int b[N][N]; vector <int> vec1,vec2; int main(){ ios::sync_with_stdio(false);cin.tie(0); cin>>n>>m; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) cin>>a[i][j]; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) cin>>b[i][j]; int last=0; for(int k=2;k<=n+m;k++){ vec1.clear();vec2.clear(); int i,j; if(k<=1+m) i=1; else i=k-m; j = k-i; //cout<<endl<<endl; while(i<k && i<=n && j>=1){ //cout<<i<<" "<<j<<endl; vec1.push_back(a[i][j]); vec2.push_back(b[i][j]); i++;j--; } sort(vec1.begin(),vec1.end()); sort(vec2.begin(),vec2.end()); int s=vec1.size(); for(i=0;i<s;i++){ if(vec1[i]!=vec2[i]){ cout<<"NO"; return 0; } } } cout<<"YES"; return 0; }
D. Nastya Is Buying Lunch
题意:
有n个人从左到右站立,然后有m个规则:每个规则里面描述的就是,如果有一个人在另一个人前面,那么这两个人就可以交换位置。问最后一个人最多可以到多前面去。
题解:
这个题我一开始乱写一发过了70个点,后面认真想了下,就WA4了。。
其实这个题可以发现一个数量关系,如果一个人目前的位置在pos处,那么如果后面有n-pos个人可以和他交换,那么他就肯定能和最后一个人交换。
当然这里的n-pos是除开某些已经和最后一个人交换了的人的。所以从后扫一下就行了,具体见代码吧。
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 5e5+5; int n,m; int a[N],cnt[N],p[N]; vector <int> g[N]; void adde(int u){ for(auto v:g[u]) cnt[v]++; } int main(){ ios::sync_with_stdio(false);cin.tie(0); cin>>n>>m; for(int i=1;i<=n;i++) cin>>a[i],p[a[i]]=i; for(int i=1;i<=m;i++){ int u,v; cin>>u>>v; g[v].push_back(u); } int ans = 0; adde(a[n]); for(int i=n-1;i>=1;i--){ if(cnt[a[i]]==n-i-ans) ans++; else adde(a[i]); } cout<<ans; return 0; }
E. Nastya Hasn't Written a Legend
题意:
给出n个数,然后给出n个数的权值以及对应的ki,现在有两种操作,一个是询问区间和,另一个就是对某个位置上的数加上x,然后对于所有后面的数,如果ai+1<ai+ki,就让ai+1=ai+ki。
题解:
这个题需要转化一下,因为题目一开始保证了ai+1>=ai+ki的,然后令ti=k1+k2+...+ki,那么两边同时减去ti,最后变为ai+1-ti>=ai-ti-1,然后我们构造bi=ai-ti-1,所以题目中的条件就转化为了bi+1>=bi。
接下来再看题目中的操作,求和操作很简单,求出对应bi的和,然后减去加上的值就行了;然后就是加法操作,如果bi加上x,那么后面小于bi+x的都要变为bi+x,这里用线段树查询一下第一个大于等于bi+x的位置就行了,具体方法就是先看左子树,再看右子树。
这个构造方法可行的原因就在于,如果有ai+1<ai+x+ki,那么也必然有bi+1<bi+x,这个比较显然,同时减去一个数并不改变等号。
代码如下:
#include <bits/stdc++.h> #define INF 999999999999999 using namespace std; typedef long long ll; const int N = 2e5+5; int n; ll a[N],b[N],t[N],k[N],pre[N]; struct Seg_Tre{ int l,r; ll lazy,sum,mx; }tre[N<<2]; void push_up(int rt){ tre[rt].sum=tre[rt<<1].sum+tre[rt<<1|1].sum; tre[rt].mx=max(tre[rt<<1].mx,tre[rt<<1|1].mx); } void push_down(int rt){ if(tre[rt].lazy!=INF){ ll lzy=tre[rt].lazy; tre[rt<<1].sum=lzy*(tre[rt<<1].r-tre[rt<<1].l+1); tre[rt<<1|1].sum=lzy*(tre[rt<<1|1].r-tre[rt<<1|1].l+1); tre[rt<<1].mx=lzy; tre[rt<<1|1].mx=lzy; tre[rt].lazy=INF; tre[rt<<1].lazy=lzy;tre[rt<<1|1].lazy=lzy; } } void build(int rt,int l,int r){ tre[rt].l=l;tre[rt].r=r;tre[rt].lazy=INF; if(l==r){ tre[rt].sum=tre[rt].mx=b[l]; return ; } int mid = l+r>>1; build(rt<<1,l,mid);build(rt<<1|1,mid+1,r); push_up(rt); } ll query_sum(int rt,int l,int r){ int L=tre[rt].l,R=tre[rt].r;int mid=L+R>>1; if(l<=L&&R<=r){ return tre[rt].sum; } push_down(rt); ll ans = 0; if(l<=mid) ans+=query_sum(rt<<1,l,r); if(r>mid) ans+=query_sum(rt<<1|1,l,r); push_up(rt); return ans ; } ll query_pos(int rt,ll val){ int L=tre[rt].l,R=tre[rt].r; int mid=L+R>>1; if(L==R) return L; ll p; push_down(rt); if(tre[rt<<1].mx>=val) p=query_pos(rt<<1,val); else p=query_pos(rt<<1|1,val); return p; } void update(int rt,int l,int r,ll val){ int L=tre[rt].l,R=tre[rt].r; int mid=L+R>>1; if(l<=L&&R<=r){ tre[rt].lazy=val; tre[rt].sum=val*(R-L+1); tre[rt].mx=val; return ; } push_down(rt); if(l<=mid) update(rt<<1,l,r,val); if(r>mid) update(rt<<1|1,l,r,val); push_up(rt); } int main(){ ios::sync_with_stdio(false);cin.tie(0); cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; for(int i=1;i<n;i++){ cin>>k[i]; k[i]+=k[i-1]; pre[i]=pre[i-1]+k[i]; } for(int i=1;i<=n;i++) b[i]=a[i]-k[i-1]; build(1,1,n); int q,x,y;char c; cin>>q; while(q--){ //getchar(); cin>>c>>x>>y; if(c=='s'){ ll ans = query_sum(1,x,y); cout<<ans+pre[y-1]-(x>=2?pre[x-2]:0)<< '\n'; }else{ ll st = query_sum(1,n,n); ll now = query_sum(1,x,x)+y; ll r = query_pos(1,now); if(r==n && st<now) r=n+1; update(1,x,r-1,now); //cout<<x<<" "<<r<<" "<<now+y<< '\n'; } } return 0; }