CF 463A && 463B 贪心 && 463C 霍夫曼树 && 463D 树形dp && 463E 线段树

http://codeforces.com/contest/462

A:Appleman and Easy Task

要求是否全部的字符都挨着偶数个'o'

#include <cstdio>

using namespace std;

char maz[101][101];

int n;

int cnt[101][101];

const int dx[4]={0,0,-1,1};

const int dy[4]={1,-1,0,0};

int main(){

    scanf("%d",&n);

    gets(maz[0]);

    for(int i=0;i<n;i++){

        gets(maz[i]);

    }

    for(int i=0;i<n;i++){

        for(int j=0;j<n;j++){

            if(maz[i][j]=='o'){

                    for(int k=0;k<4;k++){

                    int nx=i+dx[k];

                    int ny=j+dy[k];

                    if(nx<n&&ny<n&&nx>=0&&ny>=0){cnt[nx][ny]++;}

                }

            }

        }

    }

    for(int i=0;i<n;i++){

        for(int j=0;j<n;j++){

            if(cnt[i][j]&1){puts("NO");return 0;}

        }

    }

    puts("YES");

    return 0;

}
View Code

B:Appleman and Card Game

贪心

#include <cstdio>

#include <algorithm>

using namespace std;

long long n,k;

long long cnt[26];

char maz[200001];

int main(){

    scanf("%I64d%I64d",&n,&k);

    gets(maz);

    gets(maz);

    for(int i=0;i<n;i++)cnt[maz[i]-'A']++;

    sort(cnt,cnt+26);

    long long  ans=0;

    for(int i=25;i>=0;i--){

        if(cnt[i]>=k){

            ans+=k*k;

            k=0;

        }

        else {

            k-=cnt[i];

            ans+=cnt[i]*cnt[i];

        }

        if(k==0)break;

    }

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

    return 0;

}
View Code

 

题解意思:
这是一道霍夫曼编码的反问题,把所有边权取反就是霍夫曼树问题,详细证明请看算法导论,大概证明一下:
可以把每次的拿来拆分的每一段的都作为树上的节点,长度为1的值必然是叶节点,会在统计一次后被抛出,这时父节点权值则是子节点权值之和,而且必然是一颗二叉树,这与题目是符合的,也就是说深度越大的叶节点加和的次数越多,那么把最大的那两个节点先放在树里,那么它们的父节点就拥有两个节点之和的性质,那么再添加次大的,如此构建就形成了整棵最优树,(算法导论可以严格证明这是棵最优树,通过反证法),于是除了最大的两个点加了n次,其余的点分别加了n-1,n-2,....2次
#include <cstdio>

#include <algorithm>

using namespace std;

const int maxn=300005;

int n;

long long a[maxn];

int main(){

    scanf("%d",&n);

    long long ans=0,sum=0;

    for(int i=0;i<n;i++){scanf("%I64d",a+i);sum+=a[i];}

    sort(a,a+n);

    ans=sum;

    for(int i=1;i<n;i++){

        ans+=sum;

        sum-=a[i-1];

    }

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

    return 0;

}
View Code

 

D:Appleman and Tree
树形dp
这是一道树形dp,我一开始想到奇怪的地方去了,什么连通域之类的
对每个点,dp记录使其的所有子树成为单黑或纯白的方式,最后再加上自身的黑/白属性
具体来说
初始化:子树单黑=0,子树纯白=1
单黑=现有单黑*子树纯白+子树单黑*现有纯白
纯白=现有纯白*子树纯白
然后加入自身的属性
如果自身为黑,那么单黑=子树纯白(现有黑:切掉所有子节点成为0)
如果自身为白,那么纯白*=子树纯白(切掉本身使得孤立的方式更多)
#include<cstdio>

#include <cstring>

#include <algorithm>

#include <vector>

using  namespace std;

const int maxn=100005;

const long long mod=1000000007 ;

long long dp[maxn][2];

int color[maxn];

vector<int> G[maxn];

bool vis[maxn];

void  dfs(int s){

    dp[s][0]=1;

    dp[s][1]=0;

    vis[s]=true;

    for(int i=0;i<G[s].size();i++){

        int t=G[s][i];

        if(vis[t])continue;

        dfs(t);

        dp[s][1]=(dp[s][0]*dp[t][1]+dp[s][1]*dp[t][0])%mod;

        dp[s][0]=dp[s][0]*dp[t][0]%mod;

    }

    if(color[s]==1){

        dp[s][1]=dp[s][0];

    }

    else dp[s][0]+=dp[s][1];

    dp[s][1]%=mod;

    dp[s][0]%=mod;

}

int main(){

    int n;

    scanf("%d",&n);

    for(int i=1;i<n;i++){

        int temp;

        scanf("%d",&temp);

        G[temp].push_back(i);

    }

    for(int i=0;i<n;i++)scanf("%d",color+i);

    dfs(0);

    printf("%I64d\n",dp[0][1]);

    return 0;

}
View Code

E:Appleman and a Sheet of Paper

线段树
翻折,当长度>len/2的时候那么就反向翻折,这时候相当于反向了一次,需要反着来统计
看上去思路很清晰但是很麻烦
#include<cstdio>

#include <cstring>

#include <algorithm>

#include <vector>

using  namespace std;

const int maxn=100005;

const int maxnode=400005;

long long w[maxnode];

int cur,s,e,len,n,q;

int num(int i){//返回正确的标号,传入的是从开始的端点所需经过距离

    if(cur==0){return s+i;}

    return e-i;

}

void update(int k,int d){//更新线段树

    int tk=k;

    k+=n-1;

    w[k]+=d;

    while(k>0){

        k=(k-1)/2;

        w[k]+=d;

    }

}

void inv(int l){//翻转

    int tl;

    if(l*2>len){tl=len-l;cur^=1;}//翻转统计的同时就要反向更新了

    else tl=l;

    for(int i=0;i<tl;i++){

        int td=num(tl*2-1-i);

        int td2=num(i);

        update(td,w[td2+n-1]);

        update(td2,-w[td2+n-1]);

    }

    if(cur)e-=tl;else s+=tl;

    len=e-s+1;

}

long long query(int a,int b,int k,int l,int r){

    if(r<=a||l>=b||l>=r)return 0;

    if(a<=l&&r<=b){

            return w[k];

    }

    else {

        long long v1=query(a,b,k*2+1,l,(l+r)/2);

        long long v2=query(a,b,k*2+2,(l+r)/2,r);

        return v1+v2;

    }

}

int main(){

    scanf("%d%d",&n,&q);int tn=n,ttn=n;n=1;

    while(tn>0){n<<=1;tn>>=1;}

    for(int i=0;i<ttn;i++)update(i,1);

    s=0;e=ttn-1;len=ttn;

    while(q--){

        int op;

        scanf("%d",&op);

        if(op==1){

            int l;

            scanf("%d",&l);

            inv(l);

        }

        else{

            int l,r;

            scanf("%d%d",&l,&r);

           if(cur){

               l=num(l-1);//这里卡我半天因为题目给的是从s开始的序号所以从e开始就要+1,传进的参数要-1

                r=num(r-1);

            }

            else {

                l=num(l);

                r=num(r);

            }

            if(l>r)swap(l,r);

            long long ans=query(l,r,0,0,n);

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

        }



    }

}

 

你可能感兴趣的:(线段树)