题目链接:http://codeforces.com/contest/1136
A. Nastya Is Reading a Book
水题。
#include <bits/stdc++.h>
#define fi first
#define se second
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
using namespace std;
typedef long long ll;
typedef pair <int,ll> pa;
const int mx = 1e3 + 10;
int a[mx],b[mx];
int main()
{
int n,k;
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d%d",a+i,b+i);
scanf("%d",&k);
for(int i=1;i<=n;i++) if(k<=b[i])
return 0*printf("%d\n",n-i+1);
return 0;
}
B. Nastya Is Playing Computer Games
很显然n个沙井只有一个会出现扔两块石头的情况。因为但第一个沙井被打开时,其他石头都往那边扔就好了。
扔石头和开井盖的次数就是3+2*(n-1),移动次数就是(n-1)+min(k-1,n-k),整合为3*n + min(k-1,n-k)
#include <bits/stdc++.h>
#define fi first
#define se second
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
using namespace std;
typedef long long ll;
typedef pair <int,ll> pa;
const int mx = 1e5 +10;
int main()
{
int n,k;
scanf("%d%d",&n,&k);
printf("%d\n",3*n+min(k-1,n-k));//2*(n-1)+3+(n-1)+min(k-1,n-k)
return 0;
}
C. Nastya Is Transposing Matrices
将二维数组斜着看,枚举n+m-1个斜边,保证a中每个斜边中的数是b的一个排列,那么就有解,否则无解。
(试着从上到下从左到右看每个点能更新的情况就能推出)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<set>
#include<map>
#include<cstring>
using namespace std;
typedef long long ll;
const int mx = 1e3 + 10;
int a[mx][mx],b[mx][mx];
map <int,int> mp;
int main(){
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%d",a[i]+j);
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%d",b[i]+j);
}
}
int x , y;
for(int i=1;i<=m;i++){
x = 1, y = i;
mp.clear();
while(y&&x<=n) mp[a[x][y]]++,x++,y--;
x = 1,y = i;
while(y&&x<=n){
if(!mp[b[x][y]]) return 0*puts("NO");
mp[b[x][y]]--;
x++ , y--;
}
}
for(int i=2;i<=n;i++){
x = i, y = m;
mp.clear();
while(y&&x<=n) mp[a[x][y]]++,x++,y--;
x = i, y = m;
while(y&&x<=n){
if(!mp[b[x][y]]) return 0*puts("NO");
mp[b[x][y]]--;
x++ , y--;
}
}
puts("YES");
return 0;
}
D. Nastya Is Buying Lunch
很明显,愿意和主角并且可以换成功的人都会在主角排名的后面。
假设主角现在在i位置,j位置的人想和他换,那么就必须和(i,j)中的人一个个换过去,并且这些都是没有和主角成功交换的。
所以做法就很显然了。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<cstring>
using namespace std;
typedef long long ll;
const int mx = 3e5 + 10;
vector <int> g[mx];
int a[mx],pos[mx];
bool vis[mx];
int main(){
int n,m,u,v;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",a+i),pos[a[i]] = i;
for(int i=0;i<m;i++){
scanf("%d%d",&u,&v);
g[u].push_back(v);
}
int ans = 0;
for(int i=n-1;i;i--){
u = a[i];
int c = 0;
for(int v:g[u]){
if(pos[v]>pos[u]&&!vis[v])
c++;
}
if(n-i==c+ans) ans++,vis[u] = 1;
}
printf("%d\n",ans);
return 0;
}
E. Nastya Hasn't Written a Legend
本题做法"暴力寻找更新区间"+懒人线段树区间维护
设在l处给a[l]加上x,如果a[l]+x+k[i] > a[i+1],那么a[i+1]就会被赋值a[i]+x+k[i]。设v[i]表示a[i+1]-a[i]-k[i]。
那就原来的a[i+1]而言,a[i+1]增加了x-v[i],如果a[i+1]+k[i+1] > a[i+2],那么就原来的a[i+2]而言,a[i+2]增加了x-v[i]-v[i+1]。
所以如果在l出加上x,会被更新的区间就是将l看做起始点,v[i]前缀和最后一个<=x的那个。
那么就二分?NO,NO,NO。如果此次的更新使得a[i+1]==a[i]+k[i],我们可以将它看做在i处处于"平衡",这里的v[i]就是0了,可以不用去计算,直接跳过,又在l处更新只会破坏l-1的平衡点,使得v[l-1]>0,所以最多也就每次新插入一个点值,所以这个完全可以用一个set来"暴力"找出l出更新的"r"在哪里。set中只保存v[i]>0的点,最多插入q+n次,所以这个操作是q*log(n)的。
接下来我们就知道了需要更新的区间了,那么怎么更新呢?假设需要更新[l,r],那么有:
a[l] = a[l],a[l+1] = a[l] + k[l],a[l+2] = a[l] + k[i] + k[i+1],a[l+3] = a[l] + k[i] + k[i+1] + k[i+2],a[l+4]=....
直接看代码吧,类似前缀和加减(应该都看得懂)
for(int i=n-1;i>=0;i--){
suf[i] = suf[i+1] + (n-i)*b[i];//以i点为起始点的前缀和的前缀和
c[i] = c[i+1] + b[i];//以i点为起始点的前缀和
}
ll Get(int l,int r,int hd,ll a[hd]){
ll v = c[hd] - c[l-1];
ll u = suf[l-1] - suf[r] - (n-r)*(c[l-1]-c[r]);
return (a[hd]+v)*(r-l+1) + u;
}
这样就能快速算出区间[l,r]的和。
接下来懒人标记什么的基本操作用上就ok了。
#include <bits/stdc++.h>
#define fi first
#define se second
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
using namespace std;
typedef long long ll;
typedef pair <int,ll> pa;
const int mx = 1e5 +10;
ll a[mx],b[mx],add[mx];
int n,q,L,R;
ll suf[mx],c[mx];
set <pa> st;
struct node
{
int head;
ll sum,d;
}s[mx<<2];
void build(int l,int r,int rt)
{
if(l==r){
s[rt].sum = a[l];
s[rt].head = l;
return ;
}
int mid = (l+r)>>1;
build(lson);build(rson);
s[rt].sum = s[rt<<1].sum + s[rt<<1|1].sum;
}
ll Get(int l,int r,int hd,ll d){
ll v = c[hd] - c[l-1],u = suf[l-1] - suf[r] - (n-r)*(c[l-1]-c[r]);
return (d+v)*(r-l+1) + u;
}
void push_down(int l,int r,int rt)
{
if(s[rt].head){
int ls = rt<<1,rs = rt<<1|1;
s[ls].head = s[rs].head = s[rt].head;
s[ls].d = s[rs].d = s[rt].d;
int mid = (l+r)>>1;
s[ls].sum = Get(l,mid,s[rt].head,s[rt].d);
s[rs].sum = Get(mid+1,r,s[rt].head,s[rt].d);
s[rt].head = 0;
}
}
void update(int l,int r,int rt,int hd)
{
if(L<=l&&r<=R){
s[rt].head = hd;
s[rt].d = a[hd];
s[rt].sum = Get(l,r,hd,a[hd]);
return ;
}
push_down(l,r,rt);
int mid = (l+r)>>1;
if(L<=mid) update(lson,hd);
if(R>mid) update(rson,hd);
s[rt].sum = s[rt<<1].sum + s[rt<<1|1].sum;
}
ll query(int l,int r,int rt)
{
if(L<=l&&r<=R) return s[rt].sum;
int mid = (l+r)>>1;
push_down(l,r,rt);
ll ans = 0;
if(L<=mid) ans += query(lson);
if(R>mid) ans += query(rson);
return ans;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%lld",a+i);
for(int i=1;i<n;i++) scanf("%lld",b+i);
for(int i=n-1;i>=0;i--){
suf[i] = suf[i+1] + (n-i)*b[i];
c[i] = c[i+1] + b[i];
}
for(int i=2;i<=n;i++){
int v = a[i] - a[i-1] - b[i-1];
if(v) st.insert(pa(i,v));
}
build(1,n,1);
scanf("%d",&q);
char c[5];
int l,x;
while(q--){
scanf("%s%d%d",c,&l,&x);
if(c[0]=='+'){
auto it = st.lower_bound(pa(l,0));
if(l!=1){
if(it==st.end()||it->fi!=l) st.insert(pa(l,x));
else{
pa now = *it;
st.erase(it),st.insert(pa(l,x+now.se));
}
it = st.lower_bound(pa(l,0)),it++;
}
L = R = l,a[l] = query(1,n,1) + x;
while(it!=st.end()&&x>it->se)
x -= it->se,st.erase(it++);
if(it!=st.end()){
pa now = *it;
st.erase(it);
st.insert(pa(now.fi,now.se-x));
R = now.fi - 1;
}else R = n;
update(1,n,1,l);
}else L = l,R = x,printf("%lld\n",query(1,n,1));
}
return 0;
}