Codeforces Educational Codeforces Round 22 A B C E

题目链接:http://codeforces.com/contest/813


Problem A

计算做完所有题的时间总和sum,如果sum出现在某段区间内,那么最早时间就是sum,否则就是最靠近sum的之后开启提交的时间。

代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<map>
#include<cmath>
#include<set>
#define ll long long
#define for0(i,a,b) for (int i=a;i<b;i++)
#define for1(i,a,b) for (int i=a;i<=b;i++)
#define debug(x) printf("----Line%s----\n",#x)

using namespace std;

const int N = 1e5+5;



int main()
{
    //freopen("C:/Users/DELL/Desktop/input.txt", "r", stdin);
    //freopen("C:/Users/DELL/Desktop/output.txt", "w", stdout);
    //int T;scanf("%d",&T);while (T--){}
    //int n,m;while (~scanf("%d %d",&n,&m)){}
    int n,x,cnt=0,maxtime=-1;
    scanf("%d",&n);
    for1(i,1,n) scanf("%d",&x),cnt+=x;
    int m,l,r,zao=100000000;
    scanf("%d",&m);
    for1(i,1,m){
        scanf("%d %d",&l,&r);
        if (l<=cnt && cnt<=r) zao = cnt;
        if (l>=cnt) zao = min(zao,l);
        maxtime = max(maxtime,r);
    }
    if (cnt<=maxtime){
        printf("%d\n",zao);
    }
    else printf("-1\n");
    return 0;
}

Problem B

我们发现算x^{a}很快就会超过1e18,所以我们枚举出x,y所有小于1e18的可能情况,每个数最多五六十个,注意别把x^{0},y^{0}漏掉了

然后我们统计出两个数的和所有可能出现的情况记做cant[],两层五六十的for循环搞定,我们对所有值排序去重,然后开头添一个0,结尾添一个1e18+1

于是我们的区间就被分成了

0 - cant[1] - cant[2] -cant[3] - .........-1e18+1

这都是不能取的边界,然后每次枚举一段区间,分析[l,r]六种情况(根据当前枚举左右边界跟l,r的关系)就可以了。

代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<map>
#include<cmath>
#include<set>
#define ll long long
#define for0(i,a,b) for (int i=a;i<b;i++)
#define for1(i,a,b) for (int i=a;i<=b;i++)
#define debug(x) printf("----Line%s----\n",#x)

const ll INF = (ll)1e18;

using namespace std;

const int N = 1e5+5;

ll cant[50000];
ll xx[5000],yy[5000];

int main()
{
    //freopen("C:/Users/DELL/Desktop/input.txt", "r", stdin);
    //freopen("C:/Users/DELL/Desktop/output.txt", "w", stdout);
    //int T;scanf("%d",&T);while (T--){}
    //int n,m;while (~scanf("%d %d",&n,&m)){}
    ll x,y,l,r;
    while (~scanf("%I64d %I64d %I64d %I64d",&x,&y,&l,&r)){
    //if (y==INF) printf("YES\n");
    int totx=0,toty=0,tot=1;
    ///枚举所有x的单独情况
    ll now = x;
    xx[totx++] = x;//debug(333);
    xx[totx++] = 1;//debug(444);
    while (now<=INF/x){//debug(555);
        now = now*x;
        xx[totx++] = now;
    }//debug(666);
    ///枚举y
    now = y;
    yy[toty++] = y;
    yy[toty++] = 1;
    while (now<=INF/y){
        now = now*y;
        yy[toty++] = now;
    }//debug(777);
    ///枚举所有不可以的值
    //for0(i,0,totx) cant[tot++] = xx[i];
    //for0(i,0,toty) cant[tot++] = yy[i];

    for0(i,0,totx){
        for0(j,0,toty){
            if (xx[i] <= INF-yy[j]){
                cant[tot++] = xx[i]+yy[j];
            }
        }
    }//debug(888);

    sort(cant+1,cant+tot);///只是从1开始,右界不要习惯性+1
    int cnt = unique(cant+1,cant+tot)-cant;

    //for0(i,0,cnt) printf("cant[%d]=%I64d\n",i,cant[i]);

    ll maxlen = 0;

    cant[cnt++] = INF+1;

    for0(i,1,cnt){ ///(cant[i-1],cant[i]) 找最大值
        if (l>=cant[i]){
            continue;
        }
        else if (r<=cant[i-1]){
            continue;
        }
        else if (l>cant[i-1] && r<cant[i]){
            maxlen = r-l+1;
            break;
        }
        else if (l<=cant[i-1] && r>=cant[i]){
            maxlen = max(maxlen,cant[i]-1-(cant[i-1]+1)+1);
        }
        else if (l<=cant[i-1] && r<cant[i]){
            maxlen = max(maxlen,r-(cant[i-1]+1)+1);
        }
        else if (r>=cant[i] && l<cant[i]){
            maxlen = max(maxlen,cant[i]-1-l+1);
        }
    }

    printf("%I64d\n",maxlen);

    }
    return 0;
}

Problem C

小A的出发点对树进行一次遍历,记录每个点的深度,理解为A在第几个时刻就可以到达这个点。

然后从B出发遍历一次树,遍历的时候记录当前走了多少步,如果到达的这个点的深度小于等于B已经走的步数,说明走这个点就被抓住了,直接return,我们找寻整个过程到达的深度最大的点(也有可能就是出发点最深,那么就坐以待毙就行了)

最后  最深*2 就是两个人的总共步数了

代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<map>
#include<cmath>
#include<set>
#define ll long long
#define for0(i,a,b) for (int i=a;i<b;i++)
#define for1(i,a,b) for (int i=a;i<=b;i++)
#define debug(x) printf("----Line%s----\n",#x)

using namespace std;

const int N = 2e5+5;

struct node{
    int to,last;
}edge[N<<1];
int head[N],id=1;

void add(int u,int v){edge[id].to = v;edge[id].last=head[u];head[u]=id++;}

int dep[N],n,x;///记录深度

void predfs(int now,int pre,int depth)///从1节点遍历,得出所有节点的深度
{
    dep[now] = depth;
    for (int i=head[now];i!=0;i=edge[i].last){
        int v = edge[i].to;
        if (v!=pre) predfs(v,now,depth+1);
    }
}

int maxdepth=dep[x];
void dfs(int now,int pre,int cnt)///在满足当前累计步数<=当前节点深度时求出B能到达的最大深度:坐以待毙/找到能到达的最深的地方
{
    if (cnt>=dep[now]) return ;
    maxdepth = max(maxdepth,dep[now]);
    for (int i=head[now];i!=0;i=edge[i].last){
        int v = edge[i].to;
        if (v!=pre) dfs(v,now,cnt+1);
    }
}

int main()
{
    //freopen("C:/Users/DELL/Desktop/input.txt", "r", stdin);
    //freopen("C:/Users/DELL/Desktop/output.txt", "w", stdout);
    //int T;scanf("%d",&T);while (T--){}
    //int n,m;while (~scanf("%d %d",&n,&m)){}
    scanf("%d %d",&n,&x);
    for1(i,1,n-1){
        int u,v;
        scanf("%d %d",&u,&v);
        add(u,v);
        add(v,u);
    }

    predfs(1,1,-1);
    dfs(x,x,-1);

    printf("%d\n",(maxdepth+1)*2);
    return 0;
}

Problem E

每个位置的数预处理出该类型往前k个的那个数的位置,如果不存在就就等于-1,这个数记做val[]

这个可以O(N)实现,然后每次我们只要统计询问区间内val[]小于l的数就行了,因为小于l证明当前位置,不管是哪种士兵,他反正没有取够k个。

用一下分块,每个块维护块内val递增序列,完整的块二分找位置,边界暴力就行。

复杂度大概是O( q*sqrt(N)*logN)

代码:

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
#define ll long long
#define pb push_back
#define for0(i,a,b) for (int i=a;i<b;i++)
#define for1(i,a,b) for (int i=a;i<=b;i++)

using namespace std;

const int N = 1e5+5;
const int M = 400;

int n,k,blo,bl[N];
vector<int>pos[N],v[M];
int val[N];

int query(int l,int r)
{
    int ans = 0;
    for (int i=l;i<=min(bl[l]*blo,r);i++)
        if (val[i]<l) ans++;
    if (bl[l]!=bl[r]){
        for (int i=(bl[r]-1)*blo+1;i<=r;i++)
            if (val[i]<l) ans++;
    }
    for (int i=bl[l]+1;i<=bl[r]-1;i++){
        ans += lower_bound(v[i].begin(),v[i].end(),l)-v[i].begin();
    }
    return ans;
}

int main()
{
    scanf("%d %d",&n,&k);
    blo = sqrt(n);
    int maxval = 0;
    for1(i,1,n){
        scanf("%d",val+i);
        bl[i] = (i-1)/blo+1;
        pos[val[i]].pb(i);
        //printf("pos[%d].pb(%d)\n",val[i],pos[val[i]][0]);
        maxval = max(maxval,val[i]);
    }

    for1(i,1,maxval){///千万不要只搜索到n!!!!
        if (pos[i].empty()) continue;
        for (int j=0;j<pos[i].size();j++){
            if (j<k) val[pos[i][j]] = -1;//,printf("val[%d]=%d",pos[i][j],val[pos[i][j]]);
            else val[pos[i][j]] = pos[i][j-k];
        }
    }

    for1(i,1,n){
        v[bl[i]].pb(val[i]);
        //printf("val[%d]=%d\n",i,val[i]);
    }

    for1(i,1,bl[n]) sort(v[i].begin(),v[i].end());

    int m,l,r;
    int ans = 0;
    scanf("%d",&m);
    while (m--){
        scanf("%d %d",&l,&r);
        l = (l+ans)%n+1;
        r = (r+ans)%n+1;
        if (l>r) swap(l,r);

        //printf("l=%d r=%d\n",l,r);
        ans = query(l,r);
        printf("%d\n",ans);
    }

    return 0;
}

总结:

1.a^x我们需要清楚他很容易就超过题目的询问范文。

2.C题不可能两个点一起动,先试着解决一个

3.E题预处理数据的方法真的很秀,通过得出前推k个的位置,把问题直接转化为了区间询问的问题,这种思路值的考虑。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值