[题解]CF Round #384 (Div.2)

来源:互联网 发布:淘宝卖鞋正品的店铺 编辑:程序博客网 时间:2024/04/30 11:28

743A:Vladik and flights

题意简述

n个机场,编号为[1,n]这些机场分属两个公司,编号为01
在同家公司的机场间行动不耗代价,在不同公司的机场间移动,从ij话费|ij|的代价。
问从xy需要花费多少代价。

数据范围

1n105
ai[0,1]

思路

水。
x,y在同家公司答案为0,不同公司答案为1

代码

#include<cstdio>using namespace std;int n,a,b;char st[100010];int main(){    scanf("%d%d%d",&n,&a,&b);    scanf("%s",st+1);    if (st[a]==st[b])        printf("0");    else        printf("1");    return 0;}

743B:Chloe and the sequence

题意简述

数列a初始只有一个元素1,进行n1次操作。
i次操作将a,变为a,i+1,a,即将a复制一次再将i+1插入他们中间。
求第k个元素是多少。

数据范围

1n50
1k2n1

思路

经过观察可以发现。
答案即为k的二进制表示中最小的1的位置+1

代码

include<cstdio>using namespace std;int n;long long k;int main(){    scanf("%d%I64d",&n,&k);    for (int i=0;i<=n;i++)        if (k&(1LL<<i))        {            printf("%d",i+1);            break;        }    return 0;}

743C:Vladik and fractions

题意简述

给出n,问2n能否表示为1x+1x+1z的形式,其中x,y,z互不相等且x,y,z109

数据范围

1n104

思路

这个题比较有意思…
首先2n可以拆成1n+1n
然后瞬间联想到裂项项消……
1n1n+1=1n(n+1)
于是答案就是n,n+1,n(n+1)
一看n的范围,简直精致。

代码

#include<cstdio>using namespace std;int n;int main(){    scanf("%d",&n);    if (n==1)        printf("-1");    else        printf("%d %d %d\n",n,n+1,n*(n+1));     return 0;}

743D:Chloe and pleasant prizes

题意简述

给出一棵n个节点的带权树。
求两个不相交的子树,权值和的最大值。
如果没有这样的方案,输出1

数据范围

2n105
109ai109

思路

树形DP。
DP出mxi数组,表示以i为根的子树中,最大的子树权值和。
以一个节点为根的答案一定是它所有孩子的mx的最大值和次大值之和。
所有的答案扫一遍就行了。
注意一条链的情况无解。

代码

#include<cstdio>#include<cstring>#include<iostream>using namespace std;#define INF 1LL<<60 struct edge{    int s,t,next;}e[400010];int head[200010],cnt;void addedge(int s,int t){    e[cnt].s=s;e[cnt].t=t;e[cnt].next=head[s];head[s]=cnt++;    e[cnt].s=t;e[cnt].t=s;e[cnt].next=head[t];head[t]=cnt++;}int n,u,v;long long sum[200010],mx[200010];int size[200010],val[200010];long long ans=-INF,se;void dfs(int node,int lastfa){    sum[node]=val[node];    size[node]=0;    for (int i=head[node];i!=-1;i=e[i].next)        if (e[i].t!=lastfa)        {            dfs(e[i].t,node);            sum[node]+=sum[e[i].t];            size[node]++;        }    mx[node]=-INF;    se=-INF;    for (int i=head[node];i!=-1;i=e[i].next)        if (e[i].t!=lastfa)        {            if (mx[e[i].t]>mx[node])            {                se=mx[node];                mx[node]=mx[e[i].t];            }            else if (mx[e[i].t]>se)                se=mx[e[i].t];        }    if (size[node]>1)        ans=max(ans,mx[node]+se);    mx[node]=max(mx[node],sum[node]);}int main(){    scanf("%d",&n);    for (int i=1;i<=n;i++)        scanf("%d",&val[i]);     memset(head,0xff,sizeof(head));    cnt=0;    for (int i=1;i<n;i++)    {        scanf("%d%d",&u,&v);        addedge(u,v);    }    dfs(1,1);    if (ans==-INF)        printf("Impossible");    else        printf("%I64d",ans);    return 0;}

743E:Vladik and cards

题意简述

给出n个元素的一个序列,序列中所有元素[1,8]
问最长的满足如下条件的子序列长度:
1.子序列中所有相同元素相邻。
2.子序列中任意两种元素的个数之差的绝对值1

数据范围

1n1000
ai[1,8]

思路

二分+DP。
我们可以发现,所有元素的出现次数之可能为kk+1
k对于答案的影响是单调的。
所有我们可以二分这个k,通过DP验证答案。
如何DP?
首先预处理。
pos[i][j]表示i元素第j次出现的位置。
ord[i]表示原序列的第i个的那种元素是第几次出现。
DP数组f[i][mask]表示进行到第i个数,已经用过的数为mask的最长长度。
f[i][mask]只可能有三种状态转移:
1.i不取。
2.取i再加上它之前的同种元素共k个。
3.取i再加上它之前的同种元素共k+1个。
据此转移就可以了。
时间复杂度O(28nlog(n8))
代码

#include<cstdio>#include<iostream>#include<cstring>using namespace std;int f[1010][260];int n,lim,l,r,mid,tmp,ans,now;int seq[1010],ord[1010],cnt[10],pos[10][1010];bool judge(int x){    tmp=0;    memset(f,0xef,sizeof(f));    f[0][0]=0;    for (int i=1;i<=n;i++)        for (int j=0;j<=lim;j++)        {            f[i][j]=f[i-1][j];            if (j&(1<<seq[i]))            {                now=ord[i];                if (x>0&&now-x+1>=1)                    f[i][j]=max(f[i][j],f[pos[seq[i]][now-x+1]-1][j^(1<<seq[i])]+x);                if (now-x>=1)                    f[i][j]=max(f[i][j],f[pos[seq[i]][now-x]-1][j^(1<<seq[i])]+x+1);            }        }    if (x==0)        for (int i=0;i<=lim;i++)            tmp=max(tmp,f[n][i]);    else        tmp=f[n][lim];    return tmp>0;}int main(){    scanf("%d",&n);    lim=(1<<8)-1;    for (int i=1;i<=n;i++)    {        scanf("%d",&seq[i]);        seq[i]--;    }    for (int i=1;i<=n;i++)    {        ord[i]=++cnt[seq[i]];        pos[seq[i]][cnt[seq[i]]]=i;    }    l=0,r=n/8;    while (l<=r)    {        mid=(l+r)>>1;        if (judge(mid))             ans=tmp,l=mid+1;        else            r=mid-1;    }    printf("%d",ans);    return 0;}
0 0
原创粉丝点击