Codeforces Round #408(div.2)

Posted FallDream

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Codeforces Round #408(div.2)相关的知识,希望对你有一定的参考价值。

来自FallDream的博客,未经允许,请勿转载,谢谢。


凌晨打比赛真的难受,十一点趴下去,一觉起来感觉精神非常迷糊  脑子不好使了

------------------------------------------------------------------------------

A. Buying A House

给定n个点,每个点有一个价格。你有一些钱,你要求钱够买的情况下,离一个特殊点的最近距离。

小模拟

#include<cstdio>
#include<iostream>
#include<cstring>
#include<map>
#include<queue>
#include<algorithm>
#include<set>
#include<cmath>
#define MN 10005
#define INF 200000000
using namespace std;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<0||ch>9){if(ch==-) f=-1;ch=getchar();}
    while(ch>=0&&ch<=9){x=x*10+ch-0; ch=getchar();}
    return x*f;
}


int n,s[MN+5],m,ans=INF,k;

int abs(int x){return  x<0?-x:x;}

int main()
{
    n=read();k=read();m=read();
    for(int i=1;i<=n;i++)s[i]=read();
    for(int i=1;i<=n;i++)if(s[i]&&s[i]<=m)
        ans=min(ans,abs(k-i));
    printf("%d\n",ans*10);
    return 0;
}

B. Find The Bone
有n个位置,一个球放在位置1,有m个位置有洞。k个操作,每次交换两个位置的东西,如果球到了洞上面,他就会掉下去,求球的最后位置。

大模拟

#include<cstdio>
#include<iostream>
#include<cstring>
#include<map>
#include<queue>
#include<algorithm>
#include<set>
#include<cmath>
#define MN 1000005
#define INF 200000000
using namespace std;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<0||ch>9){if(ch==-) f=-1;ch=getchar();}
    while(ch>=0&&ch<=9){x=x*10+ch-0; ch=getchar();}
    return x*f;
}

int n,m,k,pos=1;
bool b[MN+5];

int main()
{
    n=read();m=read();k=read();
    for(int i=1;i<=m;i++)b[read()]=1;
    if(b[1])return 0*puts("1");
    for(int i=1;i<=k;i++)
    {
        int u=read(),v=read();
        if(u==pos)
        {
            pos=v;if(b[v])return 0*printf("%d\n",v);
        }
        else if(v==pos)
        {
            pos=u;if(b[u])return 0*printf("%d\n",u);
        }
    }
    printf("%d\n",pos);
    return 0;
}

C. Bank Hacking

有一棵n个节点的树,每个节点有一个权值,你要先选择一个节点,然后破坏它,与这个点连通且距离小等于2的点权值+1,然后你不断选择一个已经破坏的点的相邻节点破坏,求破坏的最大权值的最小值  n<=300000

很容易发现是选择一个点不变,与他相邻的+1,其它+2,取一个最大值。所以可以直接暴力枚举,算一下有多少个最大值和 与每个点相邻的最大值有几个  就可以轻松算出答案。

我菜,写了线段树。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<map>
#include<queue>
#include<algorithm>
#include<set>
#include<cmath>
#define ll long long
#define N 524288
#define MN 300005
#define INF 2000000000
using namespace std;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<0||ch>9){if(ch==-) f=-1;ch=getchar();}
    while(ch>=0&&ch<=9){x=x*10+ch-0; ch=getchar();}
    return x*f;
}

struct edge{int to,next;}e[MN*5];
int cnt=0,n,a[MN+5],head[MN+5],ans=INF;
int T[MN*10];

void renew (int x,int ad)
{
    T[x+=N]=ad;
    for(x>>=1;x;x>>=1)
        T[x]=max(T[x<<1],T[x<<1|1]);
}

int query(int l,int r)
{
    int sum=-INF;
    for(l+=N-1,r+=N+1;l^r^1;l>>=1,r>>=1)
    {
        if(~l&1) sum=max(sum,T[l+1]);
        if( r&1) sum=max(sum,T[r-1]);
    }
    return sum;
}

void ins(int f,int t)
{
    e[++cnt]=(edge){t,head[f]};head[f]=cnt;
    e[++cnt]=(edge){f,head[t]};head[t]=cnt;
}

int main()
{
    n=read();
    memset(T,128,sizeof(T));
    for(int i=1;i<=n;i++)
        a[i]=read(),renew(i,a[i]+2);
    for(int i=1;i<n;i++)
    {
        int x=read(),y=read();
        ins(x,y);
    }
    for(int x=1;x<=n;x++)
    {
        renew(x,a[x]);
        for(int i=head[x];i;i=e[i].next)
            renew(e[i].to,a[e[i].to]+1);
        ans=min(ans,query(1,n));
        renew(x,a[x]+2);
        for(int i=head[x];i;i=e[i].next)
            renew(e[i].to,a[e[i].to]+2);
    }
    printf("%d\n",ans);
    return 0;
}

D.给定一棵树,一些节点是特殊节点,满足所有点到最近的特殊节点的距离不超过d,你要删去尽可能多的边,并且保证这个条件依旧满足,输出删去的数量和方案。

考虑从每个特殊节点开始bfs,如果一条边到的节点已经被bfs到了,删掉它。注意去重。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<map>
#include<queue>
#include<algorithm>
#include<set>
#include<cmath>
#define ll long long
#define MN 300005
#define INF 200000000
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<0||ch>9){if(ch==-) f=-1;ch=getchar();}
    while(ch>=0&&ch<=9){x=x*10+ch-0; ch=getchar();}
    return x*f;
}

struct edge{int to,next;}e[MN*5];
int cnt=1,n,k,mx,head[MN+5],d[MN+5],num,q[MN+5],sum=0,fa[MN+5],ans[MN+5];

void ins(int f,int t)
{
    e[++cnt]=(edge){t,head[f]};head[f]=cnt;
    e[++cnt]=(edge){f,head[t]};head[t]=cnt;
}

int main()
{
    n=read();k=read();mx=read();
    memset(d,42,sizeof(d));
    for(int i=1;i<=k;i++)
    {
        int x=read();if(d[x]<INF)continue;
        q[++num]=x;d[x]=0;
    }
    for(int i=1;i<n;i++) ins(read(),read());
    for(int i=1;i<=num;i++)
        for(int j=head[q[i]];j;j=e[j].next)
        if(e[j].to!=fa[q[i]])
        {
            if(d[e[j].to]<INF) sum+=ans[j>>1]^1,ans[j>>1]=1;
            else d[q[++num]=e[j].to]=d[q[i]]+1,fa[e[j].to]=q[i];
        }
    printf("%d\n",sum);
    for(int i=1;i<n;i++)
        if(ans[i]) printf("%d ",i);
    return 0;
}

E 有n道题,你可以偷看p次答案,每次只能看连续的k道题。但是你只能看相邻的两个人,并且这些人不一定都是对的。
给出n,p,k和两个人分别有哪些题使得对的,求最多能看对(#滑稽)几道题  n,p<=1000 k<=50

考虑dp,用f[i][j][x][y]表示前i道题,看了j次,第一个人还能看x道题,第二个人还能看y道题。每次转移i后移一格之后,x,y都会-1(如果它不是0),也就是说看了这一题。当然,你也可以选择让j+1使得x和y变成k-1,也就是从这里开始看,后面的k-1道题当然都能看到啦。(感觉讲的不清不楚)

这样复杂度就是$O(npk^{2})$,不能通过此题,但是我们发现$p\geqslant 2\lceil\frac{n}{k}\rceil$的时候,我一定能看到所有题,只要这两个人有人做对了它。

这样p最大变成2n/k,复杂度降低为$O(n^{2}k)$,足够通过。空间的话,我们把第一维滚动一下呗。

#include<iostream>
#include<cstdio>
#include<cstring>
#define INF 30000
using namespace std;
inline int read()
{
    int x = 0 , f = 1; char ch = getchar();
    while(ch < 0 || ch > 9){ if(ch == -) f = -1;  ch = getchar();}
    while(ch >= 0 && ch <= 9){x = x * 10 + ch - 0;ch = getchar();}
    return x * f;
}

int n,p,k,m;
short f[2][1003][53][53];
bool s[2][1003];

inline void R(short&x,short y){if(y>x)x=y;}

int main()
{
    memset(f,128,sizeof(f));
    n=read();p=read();k=read();
    m=read();for(int i=1;i<=m;i++) s[0][read()]=1;
    m=read();for(int i=1;i<=m;i++) s[1][read()]=1;
    if(p>=(n+k-1)/k*2) 
    {
        int ans=0;
        for(int i=1;i<=n;i++) ans+=s[0][i]|s[1][i];
        return 0*printf("%d\n",ans);
    } 
    f[0][0][0][0]=0;
    for(int i=0,now=1,pre=0;i<n;i++,now^=1,pre^=1)
    {
        for(int j=0;j<=p;j++)    
        {             
            for(int x=0;x<=k;x++)
                for(int y=0;y<=k;y++)
                {
                    if(!x&&!y) 
                    {
                        R(f[now][j][0][0],f[pre][j][0][0]);
                        R(f[now][j+1][0][k-1],f[pre][j][0][0]+s[1][i+1]);
                        R(f[now][j+1][k-1][0],f[pre][j][0][0]+s[0][i+1]);
                        R(f[now][j+2][k-1][k-1],f[pre][j][0][0]+(s[0][i+1]|s[1][i+1])); 
                    } 
                    else if(!x)
                    {
                        R(f[now][j][0][y-1],f[pre][j][0][y]+s[1][i+1]);
                        R(f[now][j+1][k-1][y-1],f[pre][j][0][y]+(s[0][i+1]|s[1][i+1]));
                        R(f[now][j+2][k-1][k-1],f[pre][j][0][y]+(s[0][i+1]|s[1][i+1]));
                    }
                    else if(!y)
                    {
                          R(f[now][j][x-1][0],f[pre][j][x][0]+s[0][i+1]);
                          R(f[now][j+1][x-1][k-1],f[pre][j][x][0]+(s[0][i+1]|s[1][i+1]));
                          R(f[now][j+2][k-1][k-1],f[pre][j][x][0]+(s[0][i+1]|s[1][i+1]));
                    }    
                    else 
                        R(f[now][j][x-1][y-1],f[pre][j][x][y]+(s[1][i+1]|s[0][i+1]));
                    f[pre][j][x][y]=-INF;
                }
        }    
    }    
    short ans=0;
    for(int x=0;x<k;x++)
        for(int y=0;y<k;y++)
            R(ans,f[n&1][p][x][y]);
    printf("%d\n",(int)ans);
    return 0;
}

 

以上是关于Codeforces Round #408(div.2)的主要内容,如果未能解决你的问题,请参考以下文章

Codeforces Round #408 (Div. 2) A

Codeforces Round #408(div.2)

Codeforces Round #408 (Div. 2) B

Codeforces Round #408 (Div. 2)ABCD

Codeforces Round #408 (Div. 2)

Codeforces Round #705 (Div. 2)