【noip2014】d1解题报告

这几天一直没怎么更新博客,是因为白天一直考试,晚上一直做往年的noip,自然没空处理错题……今天晚上终于腾出手来了,那么先从好做点的2014年开始整理,蒟蒻的呕心沥血之作系列,开始更新!

2014年总体来说挺简单的,就是两个t3有点麻烦,表示d1蒟蒻都能水到260

t1:生活大爆炸版石头剪刀布

这个题纯模拟啊,先预处理下胜负关系,然后for一遍,取下当前状态进行判断就好

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int n,na,nb,ans1,ans2,a[210],b[210],d[5][5],ta=1,tb=1;
int main()
{
    scanf("%d%d%d",&n,&na,&nb);
    for(int i=1;i<=na;i++)
        scanf("%d",&a[i]);
    for(int i=1;i<=nb;i++)
        scanf("%d",&b[i]);
    d[0][1]=-1,d[0][2]=1,d[0][3]=1,d[0][4]=-1;//d[i][j]表示a出i,b出j的时候的输赢情况 
    d[1][0]=1,d[1][2]=-1,d[1][3]=1,d[1][4]=-1;
    d[2][0]=-1,d[2][1]=1,d[2][3]=-1,d[2][4]=1;
    d[3][0]=-1,d[3][1]=-1,d[3][2]=1,d[3][4]=1;
    d[4][0]=1,d[4][1]=1,d[4][2]=-1,d[4][3]=-1;
    for(int i=1;i<=n;i++)
    {
        ta=(ta-1)%na+1,tb=(tb-1)%nb+1;//直接枚举现在的状态,(x-1)/y+1是一个小技巧,这个式子的范围是1~y,就不会出现取模时候的0~y-1了 
        if(d[a[ta]][b[tb]]==1)
            ans1++;
        else if(d[a[ta]][b[tb]]==-1)
            ans2++;
        ta++,tb++;
    }
    printf("%d %d",ans1,ans2);
}

t2:联合权值

这个题是棵树,联合权值就有两种情况,一是爷爷和孙子,二是兄弟之间(写的我好虚),那么联合权值其实dfs一遍就好

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
const long long mo=10007;
long long n,x,y,tail,s[200020],w[200020],ji[200020],ans1,ans2,head[200020],mx[200020];
struct in
{
    long long to,ne;
}ter[400040];
inline void build(int f,int l)
{
    ter[++tail]=(in){l,head[f]},head[f]=tail;
    ter[++tail]=(in){f,head[l]},head[l]=tail;
}
void dfs(int x,int f)
{
    bool flag=0;
    for(int i=head[x];i>0;i=ter[i].ne)//求祖孙之间的关系 
    {
        int t=ter[i].to;
        if(t==f)
            continue;
        flag=1;
        dfs(t,x);
        s[x]+=w[t];//统计自己蛾子的w值之和 
        //cout<<s[x]<<' '<<w[t]<<endl;
        if(mx[x]<w[t])//求个最大值 
            mx[x]=w[t],ji[x]=t;//记录下哪个点是最大值 
        ans1+=((w[x]%mo)*(s[t]%mo))%mo;//权值和等于它蛾子的s乘上它本身的w值的和 
        ans2=max(ans2,w[x]*mx[t]);//取联合权值的最大值 
    }
    if(!flag)
        return;
    long long tot=s[x],m2=0;
    //cout<<tot<<' '<<x<<endl;
    for(int i=head[x];i>0;i=ter[i].ne)// 求兄弟之间的关系 
    {
        int t=ter[i].to;
        if(t==f)//不能到它的父节点上 
            continue;
        if(w[t]>m2&&ji[x]!=t)//更新次小值 
            m2=w[t]; 
        tot-=w[t];
        ans1+=((w[t]%mo)*(tot%mo))%mo;//这个点与它的所有的兄弟的联合权值之和 
    }
    ans2=max(ans2,mx[x]*m2);/**/
}
/*
这个题看似复杂,其实很简单。首先我们可以很容易的知道,这个图是一棵树
转化成树之后,只有两种情况会产生联合权值了,一是一个点与它的兄弟,二是一个点和它的孙子
所以我们可以用dfs,求每个点自己蛾子当中的w最大值mx和自己蛾子的权值和s 
在具体求的时候,我们分情况讨论
一是祖孙的情况,求的时候用自己蛾子的mx和s(很好理解)
二是兄弟的情况,求一下非严格的次大值,开始前将变量tot赋值为s,每到一个蛾子时候令tot减去蛾子的w,用tot与蛾子的w相乘(为了去重) 
最后记得*2(题目要求正着反着都可以) 
*/
int main()
{
    //freopen("link.in","r",stdin);
    //freopen("link.out","w",stdout);
    scanf("%lld",&n);
    memset(head,-1,sizeof(head));
    for(int i=1;i<=n-1;i++)
        scanf("%lld%lld",&x,&y),build(x,y);//建树 
    for(int i=1;i<=n;i++)
        scanf("%lld",&w[i]);
    dfs(1,0);//处理 
    cout<<ans2<<' '<<(ans1*2%mo);//注意最大值不能取模,因为题目只要求权值之和取模 
    fclose(stdin);
    fclose(stdout);
    return 0;
}

t3:飞扬的小鸟

这个题就是二维dp,dp[i][j]表示第i列第j行要想到达这至少需要点击几次题目,再加上个刷表法(就是一口气把一个点所有能关系到的状态都推出去),然后就可以了,之前做的时候打次了……

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio> 
using namespace std;
int n,m,k,a,b,c,dp[10010][1010],sh[10010],xi[10010],ans=2139062143,tail;
struct in
{
    int x,y;
}ter[10010];
bool flag[10010];
inline void re(int &a)
{
    a=0;
    char b=getchar();
    while(b<'0'||b>'9')
        b=getchar();
    while(b>='0'&&b<='9')
        a=a*10+b-'0',b=getchar();
}
int main()
{
    re(n),re(m),re(k);
    memset(dp,127,sizeof(dp));
    for(int i=0;i<=n-1;i++)
        re(sh[i]),re(xi[i]);
    for(int i=0;i<=n;i++)
        ter[i]=(in){1,m};
    for(int i=1;i<=k;i++)
        re(a),re(b),re(c),ter[a]=(in){b+1,c-1},flag[a]=1;//不能擦到障碍物上 
    for(int i=0;i<=m;i++)//开始一下都不用跳 
        dp[0][i]=0;
    for(int i=0;i<=n-1;i++)
        for(int j=ter[i].y;j>=ter[i].x;j--)
        {
            if(dp[i][j]>=2139062143)//如果这个地方无法转移直接跳过 
                continue;
            tail=i;//记录下最远能够到哪里 
            dp[i+1][j-xi[i]]=min(dp[i+1][j-xi[i]],dp[i][j]);//先处理下落的情况 
            for(int k=1;k<=(m-j)/sh[i]+1;k++)//这个地方运用了刷表法,个人认为就是一次性将状态推出去,因为就算是普通的dp,也是不断比较,如此我们可以直接往前推,每次推出去都比较一下,就可以成功转移了 
            {
                if(dp[i+1][min(k*sh[i]+j,m)]>dp[i][j]+k) 
                dp[i+1][min(k*sh[i]+j,m)]=dp[i][j]+k;
                else//如果现在就不能更新,那么上面的也无法更新,因为上面之前的状态也是从下面转移而来,下面都不能更新了,上面也无从说起 
                    break;
            }
        }
    for(int i=1;i<=m;i++)//取个最小值,因为只要到达n就好 
        ans=min(ans,dp[n][i]);
    if(ans<2139062143)//如果最小值有小于初始值的情况,说明能够到达答案 
        cout<<1<<'\n'<<ans;
    else//否则不能 
    {
        ans=0;
        for(int i=0;i<=tail;i++)
        {
            if(flag[i])
                ans++;
        }
        cout<<0<<'\n'<<ans;
    }
}

d1完成,下面是d2!

转载于:https://www.cnblogs.com/Loi-dfkdsmbd/articles/7788626.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值