Codeforces Round #353 (Div. 2)题解

比赛传送门:http://codeforces.com/contest/675

A:水题

判断是否在等差数列里,一定要分类讨论公差正负,b和a的大小,不能直接模


B:水题

5个位置变量设为x1到x5,发现4个正方形里都有x3,所以x3是有n种取法,接下来就是x1取定,另外3个都取定了,算出另外3个由x1表达的式子,判断是否在范围里即可


C:前缀和

给你n个数字,围成一个圈,然后总和为0,有一种操作是可以把一个数字,给周围两个数字中的一个一些值,就比如有-3 和3,3能给-3 3这么多值,然后他们就变成了0 0,这是一次操作,然后问你最少多少次操作,能变成全0

考虑一段值,和为0,他们变成全0,只需要长度-1次操作,这是肯定的,所以就是找出那些和为0的段

这个该怎么找呢,可以把前缀和都求出来,这样一段长度为n的前缀和数组,里面有些是0有些不是0,不是0都是成段出现,这连续的段的长度就是这一段需要移动的次数,所以只需要考虑长度n的前缀和数组里面不是0的个数

但是这n个数字是一个圈,所以不一定是从1开始到n,可能2到n+1之类的,所以需要把长度n补为2n,然后在里面考虑长度n的前缀和,当你考虑2到n+1的时候,应该找里面不为sum[1]的个数,因为这个前缀和是要减去sum[1]的

#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#pragma comment(linker,"/STACK:102400000,102400000")

using namespace std;
#define   MAX           200005
#define   MAXN          1000005
#define   maxnode       15
#define   sigma_size    30
#define   lson          l,m,rt<<1
#define   rson          m+1,r,rt<<1|1
#define   lrt           rt<<1
#define   rrt           rt<<1|1
#define   middle        int m=(r+l)>>1
#define   LL            long long
#define   ull           unsigned long long
#define   mem(x,v)      memset(x,v,sizeof(x))
#define   lowbit(x)     (x&-x)
#define   pii           pair<int,int>
#define   bits(a)       __builtin_popcount(a)
#define   mk            make_pair
#define   limit         10000

//const int    prime = 999983;
const int    INF   = 0x3f3f3f3f;
const LL     INFF  = 0x3f3f;
const double pi    = acos(-1.0);
const double inf   = 1e18;
const double eps   = 1e-8;
const LL    mod    = 1e9+7;
const ull    mx    = 133333331;

/*****************************************************/
inline void RI(int &x) {
      char c;
      while((c=getchar())<'0' || c>'9');
      x=c-'0';
      while((c=getchar())>='0' && c<='9') x=(x<<3)+(x<<1)+c-'0';
 }
/*****************************************************/

map<LL,int> ma;
int a[MAX];
LL sum[MAX];

int main(){
    //freopen("in.txt","r",stdin);
    int n;
    while(cin>>n){
        ma.clear();
        sum[0]=0;
        for(int i=1;i<=n;i++) scanf("%d",&a[i]),sum[i]=sum[i-1]+a[i];
        for(int i=n+1;i<=2*n;i++) sum[i]=sum[i-1]+a[i-n];
        for(int i=1;i<=n;i++){
            if(!ma.count(sum[i])) ma[sum[i]]=0;
            ma[sum[i]]++;
        }
        int maxn=ma[0];
        for(int i=n+1;i<=2*n;i++){
            ma[sum[i-n]]--;
            ma[sum[i]]++;
            maxn=max(maxn,ma[sum[i-n]]);
        }
        cout<<n-maxn<<endl;
    }
    return 0;
}


D:排序+set

构造二叉搜索树,问你按照顺序放进去每个节点,它的父节点是什么

暴力肯定会TLE辣,如果是一条链,就退化到O(n^2)了

要考虑二叉搜索树的性质,把一棵二叉搜索树投影到横轴上,数列是有序的,所以可以先排序,然后按照顺序往里面放,记录每个点的左右儿子是否都已经被占领即可,每次放进去的时候找比它小的第一个和比它大的第一个就行,然后考虑比它小的的右儿子,比它大的的左儿子,必定只有一个满足(因为小的放进去,已经占领了大的左儿子,或者大的放进去占领了小的右儿子,所以必定只有一个解)

#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#pragma comment(linker,"/STACK:102400000,102400000")

using namespace std;
#define   MAX           100005
#define   MAXN          1000005
#define   maxnode       15
#define   sigma_size    30
#define   lson          l,m,rt<<1
#define   rson          m+1,r,rt<<1|1
#define   lrt           rt<<1
#define   rrt           rt<<1|1
#define   middle        int m=(r+l)>>1
#define   LL            long long
#define   ull           unsigned long long
#define   mem(x,v)      memset(x,v,sizeof(x))
#define   lowbit(x)     (x&-x)
#define   pii           pair<int,int>
#define   bits(a)       __builtin_popcount(a)
#define   mk            make_pair
#define   limit         10000

//const int    prime = 999983;
const int    INF   = 0x3f3f3f3f;
const LL     INFF  = 0x3f3f;
const double pi    = acos(-1.0);
const double inf   = 1e18;
const double eps   = 1e-8;
const LL    mod    = 1e9+7;
const ull    mx    = 133333331;

/*****************************************************/
inline void RI(int &x) {
      char c;
      while((c=getchar())<'0' || c>'9');
      x=c-'0';
      while((c=getchar())>='0' && c<='9') x=(x<<3)+(x<<1)+c-'0';
 }
/*****************************************************/

set<int> s;
map<int,int> l,r;

int main(){
    //freopen("in.txt","r",stdin);
    int n;
    while(cin>>n){
        l.clear(),r.clear();s.clear();
        for(int i=0;i<n;i++){
            int a;
            scanf("%d",&a);
            if(i){
                set<int>::iterator L=s.lower_bound(a);
                set<int>::iterator R=s.lower_bound(a);
                if(L==s.begin()){
                    printf("%d",*R);
                    l[*R]=a;
                }
                else if(R==s.end()){
                    L--;
                    printf("%d",*L);
                    r[*L]=a;
                }
                else{
                    L--;
                    if(r[*L]){
                        printf("%d",*R);
                        l[*R]=a;
                    }
                    else{
                        printf("%d",*L);
                        r[*L]=a;
                    }
                }
            }
            s.insert(a);
            if(i){
                if(i==n-1) printf("\n");
                else printf(" ");
            }
        }
    }
    return 0;
}

E:dp+线段树

给你第i个车站,可以买到i+1到ai的车站的票,p[i,j]为从i到j的用的最少的车票,问你sigma(p[i,j])

考虑p[i]为第i个车站往后面的所有车站走的最小值,能直接到i+1到ai,所以i+1 - ai之间的车站肯定是1次就能到达,后面的那些车站如何最小化呢,应该要在i+1 - ai之间找一个过渡点,哪个最优呢,我原本考虑是p[i]最小的,但是发现并不行,因为p[i]最小,不代表去掉i能1次到达的那些站之后还是最小,所以应该考虑区间里面能到达最远地方的那个车站,因为它能走的最远,它用一次票走到的地方更多,所以它最优,线段树维护下即可

#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#pragma comment(linker,"/STACK:102400000,102400000")

using namespace std;
#define   MAX           100005
#define   MAXN          1000005
#define   maxnode       15
#define   sigma_size    30
#define   lson          l,m,rt<<1
#define   rson          m+1,r,rt<<1|1
#define   lrt           rt<<1
#define   rrt           rt<<1|1
#define   middle        int m=(r+l)>>1
#define   LL            long long
#define   ull           unsigned long long
#define   mem(x,v)      memset(x,v,sizeof(x))
#define   lowbit(x)     (x&-x)
#define   pii           pair<int,int>
#define   bits(a)       __builtin_popcount(a)
#define   mk            make_pair
#define   limit         10000

//const int    prime = 999983;
const int    INF   = 0x3f3f3f3f;
const LL     INFF  = 0x3f3f;
const double pi    = acos(-1.0);
const double inf   = 1e18;
const double eps   = 1e-8;
const LL    mod    = 1e9+7;
const ull    mx    = 133333331;

/*****************************************************/
inline void RI(int &x) {
      char c;
      while((c=getchar())<'0' || c>'9');
      x=c-'0';
      while((c=getchar())>='0' && c<='9') x=(x<<3)+(x<<1)+c-'0';
 }
/*****************************************************/

LL p[MAX];
pii maxv[MAX<<2];
int a[MAX];
void pushup(int rt){
    maxv[rt]=max(maxv[rrt],maxv[lrt]);
}
void build(int l,int r,int rt){
    if(l==r){
        maxv[rt]=mk(0,l);
        return;
    }
    middle;
    build(lson);
    build(rson);
}

void update(int l,int r,int rt,int pos,int d){
    if(l==r){
        maxv[rt]=mk(d,pos);
        return ;
    }
    middle;
    if(pos<=m) update(lson,pos,d);
    else update(rson,pos,d);
    pushup(rt);
}

pii query(int l,int r,int rt,int L,int R){
    if(L<=l&&r<=R) return maxv[rt];
    middle;
    pii u=mk(0,0);
    if(L<=m) u=max(u,query(lson,L,R));
    if(R>m) u=max(u,query(rson,L,R));
    return u;
}
int main(){
    //freopen("in.txt","r",stdin);
    int n;
    while(cin>>n){
        for(int i=1;i<n;i++) scanf("%d",&a[i]);
        p[n-1]=1;
        build(1,n,1);
        update(1,n,1,n-1,a[n-1]);
        LL ans=p[n-1];
        for(int i=n-2;i>0;i--){
            pii u=query(1,n,1,i+1,a[i]);
            p[i]=a[i]-i+(p[u.second]-(a[i]-u.second))+(n-a[i]);
            update(1,n,1,i,a[i]);
            ans+=(LL)p[i];
        }
        cout<<ans<<endl;
    }
    return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值