noip模拟题11.10 光棍节前夕

T1 A

Statement

给出一个长度不超过100只包含’B’和’R’的字符串,将其无限重复下去。
比如,BBRB则会形成BBRBBBRBBBRB
现在给出一个区间[l,r]询问该区间内有多少个字符’B’(区间下标从1开始)

Input

第一行为一个只包含’B’和’R’的字符串
第二行为两个整数,表示l和r

Output

输出[l,r]区间内字符’B’的数量

Sample Input

BBRB
4 8

Sample Output

4

Limit

1<=|S|<=100(字符串长度大于等于1,小于等于100)
1<=i<=r<=1e18

利用简单数学思想,先将区间长度中每个位置往前往后分别有多少B算出来,然后通过一系列处理算出 l 在区间的哪个位置,r 在区间的哪个位置,l r 中共有多少个区间(直接%)即可。
代码:

#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
int preb[105],nexb[105];
char s[105];
int main()
{
    freopen("A.in","r",stdin);
    freopen("A.out","w",stdout);
    long long n;
    scanf("%s",s+1);
    n=strlen(s+1);
    for(int i=1;i<=n;i++)
    {
        if(s[i]=='R')
            preb[i]=preb[i-1];
        else
            preb[i]=preb[i-1]+1;
    }
    for(int i=n;i>=1;i--)
    {
        if(s[i]=='R')
            nexb[i]=nexb[i+1];
        else
            nexb[i]=nexb[i+1]+1;
    }
    long long l,r;
    scanf(AUTO,&l);scanf(AUTO,&r);
    long long left=l%n;
    if(left==0)left=n;
    long long tmp=n-left;
    long long mid=r-(tmp+l);
    long long num=mid/n;
    mid%=n;
    long long ans=nexb[left]+num*preb[n]+preb[mid];
    printf(AUTO,ans);
    return 0;
}

T2 B

【题目描述】

我们要从n种食物选m个出来,安排一个顺序吃掉它(们),每种食物有个美味值ai,然后我们有k个规则,每个规则有 xi, yi 和 ci三个数,如果吃完第xi种食物接下来马上吃第yi种食物,第j种食物的美味值会增加ci。每种食物至多吃一个,求美味值最大的和是多少?

【输入格式】

第一行有三个数n,m,k,k代表有k个规则(0<=k<=n*(n-1))。
第二行有n个数字代表每个食物的美味值。
接下去有k行,每行三个数xi,yi,ci。保证没有任意两个规则的xi和yi同时相同。

【输出格式】

一行一个数代表答案

【sample input1】

2 2 1
1 1
2 1 1

【sample output1】

3

【sample input 2】

4 3 2
1 2 3 4
2 1 5
3 4 2

【sample output 2】

12

【数据范围】

30% m<=n<=5 ,0<=ci,ai<=1e5
100% m<=n<=18,0<=ci,ai<=1e9

dp,考试时想错了,然后一直在想,浪费了很多时间。
其实就是一个简单状压dp,开一个1<<18的数组,枚举状态推。

for(int k=0;k<n;k++)//枚举哪一个没吃
    if(((i>>k)&1)==0)//如果这一位为0表示没吃
        for(int j=0;j<n;j++)//枚举哪一个是最后吃的
            if((1<<j)&i)//如果这一位为1表示吃了
                dp[i|(1<<k)][k]=max(dp[i][j]+mp[j][k]+a[k],dp[i|(1<<k)][k]);

代码:

#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
long long add[20][20],f[19][262145],d[20];
int cnt[19][48625];
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
inline long long maxn(long long a,long long b)
{
    if(a>b)return a;
    return b;
}
int main()
{
    freopen("B.in","r",stdin);
    freopen("B.out","w",stdout);
    int n=read(),m=read(),K=read();
    for(int i=1;i<=n;++i)
        scanf(AUTO,&d[i]);
    for(int i=1;i<=K;++i)
    {
        int a=read(),b=read();
        scanf(AUTO,&add[a][b]);
    }
    for(int i=0;i<=(1<<n)-1;++i)
    {
        int p=i,num=0;
        while(p)
        {
            if((p&1)==1)
                num++;
            p=p>>1;
        }
        cnt[num][++cnt[num][0]]=i;
    }
    for(int i=1;i<=n;i++)
        f[i][1<<(i-1)]=d[i];
    for(int k=0;k<=(1<<n)-1;++k)
        for(int i=0;i<=n-1;++i)
            if(((k>>i)&1)==1)
                for(int j=0;j<=n-1;++j)
                    if(((k>>j)&1)==0)
                        f[j+1][k|(1<<j)]=max(f[j+1][k|(1<<j)],f[i+1][k]+add[i+1][j+1]+d[j+1]);
    long long ans=0;
    for(int i=1;i<=n;++i)
        for(int k=1;k<=cnt[m][0];++k)
            ans=maxn(ans,f[i][cnt[m][k]]);
    printf(AUTO,ans);
    return 0;
}

T3 C

题目描述

历史上有一个著名的王国。它的所有城市互相连通并且构成一棵树。城市1为首都也就是这棵树的根。
因为外来的入侵,国王决定在某些城市加派士兵。所有城市初始士兵数量为0。当城市 i 被加派了 k 名士兵时,城市 i 的所有子城市需要被加派 k+1 名士兵,这些子城市的所有子城市需要被加派 k+2 名士兵,以此类推。
当然,加派士兵的同时,国王也需要不断了解当前的情况。于是他随时可能询问以城市 i 为根的子树中的所有城市共被加派了多少士兵。
你现在是国王的军事大臣,你能回答出国王的每个询问么?

输入

第一行,包含两个整数N,P代表城市数量以及国王的命令的数量。
接下来的1行为N-1个数,第 i 个数表示第 i+1 号城市的父亲城市。
接下来的P行,每行代表国王的一个命令,命令分两种:
A X K 在城市X中加入K个士兵
Q X 询问以城市X为根的子树中所有士兵数量的和

输出

对于每个Q,输出答案

输入样例

7 10
1 1 2 2 5 5
Q 1
A 2 1
Q 1
Q 2
Q 5
A 5 0
Q 5
A 3 1
Q 1
Q 2

输出样例

0
11
11
8
10
14
13

数据范围

对于50%的数据,1<=N<=1000,1<=p<=300
对于100%的数据,1<=N<=50000,1<=P<=100000,1<=X<=N,0<=K<=1000

因为第二题花了太多时间,导致第三题暴力都没码好…时间分配出了问题。第三题写出来了,但还是有点不懂,边写博客边理一理思路吧。
正解:对树做一次dfs生成一个2*N的序列,L[u],R[u]分别表示刚进入子树u时的dfs序列位置和刚处理完子树u时的dfs序列位置。则l[u]到r[u]这一段便是子树u的所有节点,然后我们就可以以线段树的方法来更新。
设 u 为一个父节点,v 为该节点子树中的一个节点,如果此时 u 加了 k 那么不难发现 v 就要加 deep[v]-deep[u]+k,而对于v来说 deep[v] 是不变的,那么我们可以定义一个数组记录每次的 k-deep[u]。另外,由于每次还要加上 deep[v] 那么定义另一个数组记录 deep[v] 访问了多少次。记 dep 表示给节点及其子树的所有深度之和,tot 表示访问了多少次,delta 表示 k-deep[u] 那一坨,size 表示该节点有多少棵子树,sum1 表示:深度之和×访问次数,sum2 表示 k-deep[u] 那一坨×子树数量。由之前推出的公式,节点 u 的答案等于 Σdeep×tot+delta×size=sum1+sum2。
表示静态查错查了很久,线段树什么的最烦了。

#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define Lson(u) u<<1
#define Rson(u) (u<<1)|1
#ifdef WIN32
#define AUTO "%I64d"
#else 
#define AUTO "%lld"
#endif 
using namespace std;
struct node{
    long long sum1,sum2,tot,delta,dep,size;
}gg[200020];
int head[50005],tov[50005],nex[50005],cnt;
int deep[50005],L[50005],R[50005],n,step,q,rank[50005];
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
void add(int a,int b)
{
    tov[++cnt]=b;
    nex[cnt]=head[a];
    head[a]=cnt;
}
void dfs(int k,int dep)
{
    L[k]=++step;rank[step]=k;deep[k]=dep;
    int t=head[k];
    while(tov[t])
    {
        dfs(tov[t],dep+1);t=nex[t];
    }
    R[k]=step;
}
void pushup(int k)
{
    gg[k].dep=gg[Lson(k)].dep+gg[Rson(k)].dep;
    gg[k].size=gg[Lson(k)].size+gg[Rson(k)].size;
}
void build(int l,int r,int k)
{
    if(l==r)
    {
        gg[k].dep=deep[rank[l]];gg[k].size=1;return ;
    }
    int mid=(l+r)>>1;
    build(l,mid,Lson(k));build(mid+1,r,Rson(k));
    pushup(k);
}
void add1(int k,int val)
{
    gg[k].tot+=val;gg[k].sum1+=gg[k].dep*val;
}
void pushdown1(int k)
{
    if(!gg[k].tot)return ;
    add1(Lson(k),gg[k].tot);add1(Rson(k),gg[k].tot);
    gg[k].tot=0;
}
void pushup1(int k)
{
    gg[k].sum1=gg[Lson(k)].sum1+gg[Rson(k)].sum1;
}
void update1(int l,int r,int k,int gl,int gr,int add)
{
    if(l==gl&&r==gr)
    {
        add1(k,add);return ;
    }
    pushdown1(k);
    int mid=(l+r)>>1;
    if(mid>=gr)update1(l,mid,Lson(k),gl,gr,add);
    else if(mid<gl)update1(mid+1,r,Rson(k),gl,gr,add);
    else
    {
        update1(l,mid,Lson(k),gl,mid,add);update1(mid+1,r,Rson(k),mid+1,gr,add);
    }
    pushup1(k);
}
void add2(int k,int val)
{
    gg[k].delta+=val;gg[k].sum2+=val*gg[k].size;
}
void pushdown2(int k)
{
    if(!gg[k].delta)return ;
    add2(Lson(k),gg[k].delta);add2(Rson(k),gg[k].delta);
    gg[k].delta=0;
}
void pushup2(int k)
{
    gg[k].sum2=gg[Lson(k)].sum2+gg[Rson(k)].sum2;
}
void update2(int l,int r,int k,int gl,int gr,int add)
{
    if(l==gl&&r==gr)
    {
        add2(k,add);return ;
    }
    pushdown2(k);
    int mid=(l+r)>>1;
    if(mid>=gr)update2(l,mid,Lson(k),gl,gr,add);
    else if(mid<gl)update2(mid+1,r,Rson(k),gl,gr,add);
    else
    {
        update2(l,mid,Lson(k),gl,mid,add);update2(mid+1,r,Rson(k),mid+1,gr,add);
    }
    pushup2(k);
}
long long query(int l,int r,int k,int gl,int gr)
{
    if(l==gl&&r==gr)
        return gg[k].sum1+gg[k].sum2;
    pushdown1(k);pushdown2(k);
    int mid=(l+r)>>1;
    if(mid>=gr)return query(l,mid,Lson(k),gl,gr);
    else if(mid<gl)return query(mid+1,r,Rson(k),gl,gr);
    else
        return query(l,mid,Lson(k),gl,mid)+query(mid+1,r,Rson(k),mid+1,gr);
}
int main()
{
    freopen("C.in","r",stdin);
    freopen("C.out","w",stdout);
    n=read();q=read();deep[1]=1;
    for(int i=2;i<=n;i++)
    {
        int a=read();
        add(a,i);
    }
    dfs(1,1);build(1,n,1);
    while(q--)
    {
        char c=getchar();
        while(c!='Q'&&c!='A')
            c=getchar();
        if(c=='Q')
        {
            int a=read();printf(AUTO,query(1,n,1,L[a],R[a]));printf("\n");
        }
        else
        {
            int a=read(),b=read();
            update1(1,n,1,L[a],R[a],1);
            update2(1,n,1,L[a],R[a],b-deep[a]);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值