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

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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章