JZOJ2017.08.17 B组

59 篇文章 0 订阅

T1

Description

“封印大典启动,请出Nescafe魂珠!”随着圣主applepi一声令下,圣剑护法rainbow和魔杖护法freda将Nescafe魂珠放置于封印台上。封印台是一个树形的结构,魂珠放置的位置就是根节点(编号为0)。还有n个其他节点(编号1-n)上放置着封印石,编号为i的封印石需要从魂珠上获取Ei的能量。能量只能沿着树边从魂珠传向封印石,每条边有一个能够传递的能量上限Wi,魂珠的能量是无穷大的。作为封印开始前的准备工作,请你求出最多能满足多少颗封印台的能量需求?

注意:能量可以经过一个节点,不满足它的需求而传向下一个节点。每条边仅能传递一次能量。

Input

第一行一个整数n,表示除根节点之外的其他节点的数量。

接下来n行,第i+1行有三个整数Fi、Ei、Wi,分别表示i号节点的父节点、i号节点上封印石的能量需求、连接节点i与Fi的边最多能传递多少能量。

Output

最多能满足多少颗封印石的能量需求。

思路:

贪心

我们先以每个点的能量需求按小到大排序
那么我们就可以枚举每一个点能否走到根,求出此路径
如果能走到就将当前路径上的所有边减去能量所需

代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int n,ans,f[1010],w[1010];
struct tree{int e,b;}k[1010];
bool cmp(tree a,tree b) { return a.e<b.e; }
int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
    {
        scanf("%d%d%d",&f[i],&k[i].e,&w[i]);
        k[i].b=i;
    }
    sort(k+1,k+n+1,cmp);
    int j;
    for (int i=1;i<=n;i++)
    {
        for (j=k[i].b;j;j=f[j]) if (w[j]<k[i].e) break;
        if (j) continue;
        for (j=k[i].b;j;j=f[j]) w[j]-=k[i].e;
        ans++;
    }
    printf("%d",ans);
    return 0;
}

T2

Description

“圣主applepi于公元2011年9月创造了Nescafe,它在散发了16吃光辉之后与公元2011年11月12日被封印为一颗魂珠,贮藏于Nescafe神塔之中。公元2012年9月,圣主带领四大护法重启了Nescafe,如今已经是Nescafe之魂的第30吃传播了。不久,它就要被第二次封印,而变成一座神杯。。。”applepi思索着Nescafe的历史,准备着第二次封印。

Nescafe由n种元素组成(编号为1~n),第i种元素有一个封印区[ai,bi]。当封印力度E小于ai时,该元素获得ai的封印能量;当封印力度E在ai到bi之间时,该元素将获得E的封印能量;而当封印力度E大于bi时,该元素将被破坏从而不能获得任何封印能量。现在圣主applepi想选择恰当的E,使得封印获得的总能量尽可能高。为了封印的最后一击尽量完美,就请你写个程序帮他计算一下吧!

Input

第一行一个整数N。

接下来N行每行两个整数ai、bi,第i+1行表示第i种元素的封印区间。

Output

两个用空格隔开的证书,第一个数十能够获得最多总能量的封印力度E,第二个数是获得的总能量大小。当存在多个E能够获得最多总能量时,输出最小的E。

思路:

首先,我们可以将左端点和右端点都压成一个点,并记录它为左端点还是右端点
那么我们一开始记录一个左端点的前缀和
再记录一个m,为有多少个区间中包含这个数(由于数是从小到大排的,所以不用每次跑O(n))
那么如果当前枚举到的点是左端点,那么将前缀和-al,m++,如果取这个数的为al+m*k[i].v(为这个数的值),看能不能更新答案
如果为右端点,类似于上面,不过前缀和不变,m–(就代表当前这个区间没有值了,因为当前枚举的值大于此区间),再求取这个数的价值,看能不能更新答案

代码:

#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#include<cstdio>
#include<algorithm>
#include<functional>
#include<cstring>
using namespace std;
const int maxn=100005;
struct qj{int v,l;}k[maxn*2];
bool cmp(qj a,qj b){return a.v<b.v||a.v==b.v&&a.l>b.l;}
int a,b,n,ansn;
long long al,m,ans;
void init()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&a,&b);
        k[i*2-1].v=a;k[i*2-1].l=1;
        k[i*2].v=b;k[i*2].l=2;
        al=al+a;
    }
}
int main()
{
    init();
    sort(k+1,k+1+2*n,cmp);
    for(int i=1;i<=2*n;i++)
    { 
        long long sum=0;
        if (k[i].l==1)
        {
            al-=k[i].v;
            m++;
            sum=al+m*k[i].v;
            if (sum>ans) 
            {
                ansn=k[i].v;
                ans=sum;
            }
        }
        else
        {
            sum=al+m*k[i].v;
            if (sum>ans) 
            {
                ansn=k[i].v;
                ans=sum;
            }
            m--;
        }
    }
    printf("%d %lld",ansn,ans);
    return 0;
}

T3

Description

Input

第一行为字符串A。
第二行为字符串B。

Output

输出在B的所有循环同构串中,有多少个能够与A匹配。

思路:

hash
首先按’*’把A串分成几段,从前往后第i段给一个hash值i。
F[i,j]表示在B串的第i个位置之后,第一次出现hash值为j的串的位置。
那么怎么求出所有的F[i,j]呢?
我们枚举1~M*2-1个位置开始,向后求出于a同长的字符串的hash值,看有没有相等的
把B串前M-1个字符复制一份接在B串后面,枚举起始位置1~M,然后利用F[i,j],依次向后找A串的那些段最早出现的位置,判断最后到达的位置和起始位置的差是否超过了M即可。但是要注意如果A串开头、结尾不是’*’,要先把开头结尾处理掉。

代码:

#include<cstdio>
#include<iostream>
#include<cstring>;
#include<algorithm>
using namespace std;
unsigned long long k[110],hash[110],g[110],temp;
char a[110],b[200010];
int f[200010][52],e[110],len1,len2,l,w,ans,cd;
int main()
{
    scanf("%s",a+1);
    len1=strlen(a+1);
    hash[0]=1;
    for (int i=1;i<=len1;i++) hash[i]=hash[i-1]*13331;
    for (int i=1;i<=len1;i++)
        if (a[i]=='*')
        {
            if (i>1&&a[i-1]!='*') 
            {
                k[++l]=temp; 
                e[l]=cd;
                temp=cd=0;
            }
        }
        else 
        {
            temp+=(a[i]-'a'+1)*hash[cd++];
        }
    if (a[len1]!='*')
    {
        k[++l]=temp;
        e[l]=cd;
        temp=cd=0;
    }
    scanf("%s",b+1);
    len2=strlen(b+1);
    if (!l)
    {
        printf("%d",len2);
        return 0;
    }
    for (int i=1;i<=len2;i++) b[len2+i]=b[i];
    for (int i=1;i<=l;i++) f[len2*2+1][i]=f[len2*2+2][i]=len2*2+1;
    for (int i=len2*2;i;i--)
    {
        memset(g,-1,sizeof(g));
        g[0]=0;
        for (int j=0;j<len1;j++) if (i+j<=2*len2) g[j+1]=g[j]+(b[i+j]-'a'+1)*hash[j];
        for (int j=1;j<=l;j++)
        {
            f[i][j]=f[i+1][j];
            if (g[e[j]]==k[j]) f[i][j]=i+e[j]-1;
        }
    }
    int j,p;
    for (int i=1;i<=len2;i++)
    {
        bool flag=true;
        for (j=len1;j&&a[j]!='*';j--) if (a[j]!=b[i+len2-1-(len1-j)]) {flag=false; break;}
        if (flag==false||j==0&&len1!=len2) continue;
        for (p=1,w=i;p<=l-(a[len1]!='*');p++)
        {
            if (p==1&&a[1]!='*'&&f[i][p]+1!=i+e[1]) break;
            w=f[w][p]+1;
        }
        if (p>l-(a[len1]!='*')&&w<=i+len2-(len1-j)) ans++;
    }   
    printf("%d",ans);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值