CF#384 (Div.2) 解题报告

来源:互联网 发布:linux下解压rar文件 编辑:程序博客网 时间:2024/05/17 04:54

A

题意

有2家公司,相同公司互相传送花费为0,不同公司花费为坐标之差的绝对值。给出公司分布,问从a到b的花费最少是多少。

题解

如果ab公司相同,那么费用为0
否则的话费用为1
正确性显然

B

题意

起始数字为1,然后经过如下变换:
1
121
1213121
121312141213121

问第n-1次变换后第k个位置的数是多少

题解

可以发现这个数列满足二分的性质,那么从直接从n开始二分并且每一次都-1就可以了。
这道题的规律非常多啊。。。
注意:爆int

C

题意

给出n,求使2n=1x+1y+1z,xy yz xz的x,y,z.

题解

2n=1n+1n+1+1n(n+1).
“裂(三声)项相消法”。。

D

题意

一棵树,每个点有一个权,从中选两个互不包含的子树使子树的和最大。

题解

树形dp啊显然。。。
求出来子树的和了之后维护一个最大值和次大值就可以了。
坑点:刚开始的时候Impossible判错了,应该是-inf的问题。这种的点一定要谨慎。

#include<iostream>#include<cstring>#include<cstdio>using namespace std;#define LL long long #define N 200005const LL inf=1e18;int n,x,y;LL st,val[N],size[N],f[N][3];int tot,point[N],nxt[N*2],v[N*2];void add(int x,int y){    ++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;}void dfs(int x,int fa){    size[x]=val[x];    for (int i=point[x];i;i=nxt[i])        if (v[i]!=fa)        {            dfs(v[i],x);            size[x]+=size[v[i]];        }}void treedp(int x,int fa){    f[x][1]=size[x];    LL Max=-inf,_Max=-inf;    for (int i=point[x];i;i=nxt[i])        if (v[i]!=fa)        {            treedp(v[i],x);            f[x][1]=max(f[x][1],f[v[i]][1]);            f[x][2]=max(f[x][2],f[v[i]][2]);            if (f[v[i]][1]>=Max) _Max=Max,Max=f[v[i]][1];            else _Max=max(_Max,f[v[i]][1]);        }    if (Max!=-inf&&_Max!=-inf) f[x][2]=max(f[x][2],Max+_Max);}int main(){    scanf("%d",&n);    for (int i=1;i<=n;++i) scanf("%I64d",&val[i]);    for (int i=1;i<n;++i)    {        scanf("%d%d",&x,&y);        add(x,y);add(y,x);    }    dfs(1,0);    memset(f,128,sizeof(f));    treedp(1,0);    if (f[1][2]<=-inf) puts("Impossible");    else printf("%I64d\n",f[1][2]);}

E

题意

给出一个只有1-8的数列,求一个最长的子序列,满足这个子序列里1-8这8个数任意两数的出现次数之差的绝对值都1,并且出现的相同的数字必须连续。

题解

二分+dp。
可以发现如果1-8没有在数列里全部出现的话,那么出现的数只可以每一种选一个。这一条特判。
如果都出现过的话,每一个数的出现次数只可能有2种,并且这两个次数相差1。那么我们可以二分每一个数的出现次数中较小的那个数,然后在判断是否可行。
g(i,j,0/1)表示从i开始选了mid/mid+1个j到达的位置。f(i,j)第一维状压表示8个数选的状态为i,选到了第j个位置(下一次从这个位置开始选)的子序列最长长度。这样的话就可以转移了。
需要用-1和-inf来判断各种状态是否合法。

刚开始犯了一个错误,就是判断是否合法的时候。界限应该宽松一些,否则轻微的调整都有可能出错。

#include<algorithm>#include<iostream>#include<cstring>#include<cstdio>using namespace std;#define N 1005int n,tot,ans,Max,inf;int a[N],g[N][N][2],f[1<<9][N];bool flag[10];bool check(int mid){    memset(g,-1,sizeof(g));memset(f,128,sizeof(f));inf=f[0][0];    for (int i=1;i<=8;++i)    {        for (int j=1,cnt=0;j<=n;++j,cnt=0)            for (int k=j;k<=n;++k)            {                if (a[k]!=i) continue;                cnt++;                if (cnt==mid) g[j][i][0]=k;                if (cnt==mid+1) {g[j][i][1]=k;break;}            }    }    f[0][1]=0;    for (int i=0;i<1<<8;++i)        for (int j=1;j<=n;++j)        {            for (int k=1;k<=8;++k)            {                if ((i>>(k-1))&1) continue;                if (g[j][k][0]!=-1) f[i|(1<<(k-1))][g[j][k][0]+1]=max(f[i|(1<<(k-1))][g[j][k][0]+1],f[i][j]+mid);                if (g[j][k][1]!=-1) f[i|(1<<(k-1))][g[j][k][1]+1]=max(f[i|(1<<(k-1))][g[j][k][1]+1],f[i][j]+mid+1);            }        }    ans=inf;    for (int i=1;i<=n+1;++i)        ans=max(ans,f[(1<<8)-1][i]);    if (ans<=0) return false;    else return true;}int find(){    int l=0,r=n/8+1,mid;    while (l<=r)    {        mid=(l+r)>>1;        if (check(mid))        {            Max=max(Max,ans);            l=mid+1;        }        else r=mid-1;    }}int main(){    scanf("%d",&n);    for (int i=1;i<=n;++i)        scanf("%d",&a[i]),flag[a[i]]=true;    for (int i=1;i<=8;++i) tot+=flag[i];    if (tot<8) {printf("%d\n",tot);return 0;}    find();    printf("%d\n",Max);    return 0;}

总结

遇到奇怪的问题不要着急,特判要想清楚,尤其是-inf之类的东西。
数学题不要慌,好好搞一搞。

0 0
原创粉丝点击