[NOIP模拟][状压dp][dfs序列][线段树]

2 篇文章 0 订阅
1 篇文章 0 订阅

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

就是分两种情况,一种在两个端点一个整序列中,另一中两个端点在不同序列中。。。

#include<iostream>
#include<cstdio>
#include<ctime>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<string>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
#define INF 0x3f3f3f3f
#define clock CLOCKS_PER_SEC
#define cle(x) memset(x,0,sizeof(x))
#define maxcle(x) memset(x,127,sizeof(x))
#define mincle(x) memset(x,-1,sizeof(x))
#define minn(x1,x2,x3) min(x1,min(x2,x3))
#define cop(a,x) memcpy(x,a,sizeof(a))
#define FROP "a"
#define C(a,b) next_permutation(a,b)
#define LL long long
#define smin(x,tmp) x=min(x,tmp)
using namespace std;
const int N=105;
char s[N];
int len,bas,sum[N];
LL ans,x,y;
LL pos(LL x)
{
    LL tmp=x/len;
    if(tmp*len!=x)tmp++;
    if(tmp*len<x)return tmp-1;
    return tmp;
}
LL mod(LL x){if(x%len)return x%len;return len;}
void cal(LL l,LL r)
{
    LL ll=pos(l);
    LL rr=pos(r);
    if(ll==rr){ans=sum[mod(r)]-sum[mod(l)-1];return;}
    ans=bas*(rr-ll);
    ans+=sum[len]-sum[mod(l)-1];
    ans-=sum[len]-sum[mod(r)];
}
int main()
{
    freopen(FROP".in","r",stdin);
    freopen(FROP".out","w",stdout);
    scanf("%s",s+1);
    len=strlen(s+1);
    for(int i = 1;  i<= len; i++)
        if(s[i]=='B')sum[i]=sum[i-1]+1;
        else sum[i]=sum[i-1];
    bas=sum[len];
    scanf(AUTO AUTO,&x,&y);
    cal(x,y);
    printf(AUTO "\n",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

当看到范围为18时,多半是状压dp,,,,,,,,,,,,
具体细节看代码
其中有一个很不错的关于二进制的函数

__builtin_popcount(S)//计算一个数二进制1的个数
#include<iostream>
#include<cstdio>
#include<ctime>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<string>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
#define INF 0x3f3f3f3f
#define clock CLOCKS_PER_SEC
#define cle(x) memset(x,0,sizeof(x))
#define maxcle(x) memset(x,127,sizeof(x))
#define mincle(x) memset(x,-1,sizeof(x))
#define minn(x1,x2,x3) min(x1,min(x2,x3))
#define cop(a,x) memcpy(x,a,sizeof(a))
#define FROP "b"
#define C(a,b) next_permutation(a,b)
#define LL long long
#define smin(x,tmp) x=min(x,tmp)
#define smax(x,tmp) x=max(x,tmp)
using namespace std;
const int N=20;
int mp[N][N],val[N];
int n,m,k;
void init()
{
    scanf("%d%d%d",&n,&m,&k);
    for(int i = 1; i <= n; i++)
        scanf("%d",&val[i]);
    for(int i = 1; i <= k; i++)
    {
        int x,y,c;
        scanf("%d%d%d",&x,&y,&c);
        mp[x][y]+=c;
    }
}
LL dp[1<<N][N],ans;
int main()
{
    freopen(FROP".in","r",stdin);
    freopen(FROP".out","w",stdout);
    init();
    for(int i = 0; i <n; i++)
        dp[1<<i][i+1]=val[i+1];
    for(int S=0; S<=(1<<n);S++)
        if(__builtin_popcount(S)==m)
        {
            for(int i = 0; i< n; i++)
                if(S&(1<<i))
                smax(ans,dp[S][i+1]);
        }
        else
        {
            for(int j = 0; j < n; j++)if(S&(1<<j))
                for(int i = 0; i < n; i++)if((S&(1<<i))==0)
                    smax(dp[S|(1<<i)][i+1],dp[S][j+1]+val[i+1]+mp[j+1][i+1]);
        }
    printf(AUTO,ans);
    return 0;
}

T3:C
【题目描述】
历史上有一个著名的王国。它的所有城市互相连通并且构成一棵树。城市1
为首都也就是这棵树的根。
因为外来的入侵,国王决定在某些城市加派士兵。所有城市初始士兵数量
为0。当城市i 被加派了k 名士兵时。城市i 的所有子城市需要被加派k+1 名士
兵。这些子城市的所有子城市需要被加派k+2 名士兵。以此类推。
当然,加派士兵的同时,国王也需要不断了解当前的情况。于是他随时可
能询问以城市i 为根的子树中的所有城市共被加派了多少士兵。
你现在是国王的军事大臣,你能回答出国王的每个询问么?
【输入】
第一行,包含两个整数N,P 代表城市数量以及国王的命令的数量。
接下来的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

【解题思路】(我尽量讲清楚吧,,,实在还不懂我最下面加一个大神的链接。。。。)
50%,,,,直接暴力就好了
100%,,dfs序列+线段树,,,第一次写。。
、、、、、、分割线、、、、、、、、、、、、、、、
关于dfs序列,,求一个tii(time_in),tio(time_out),,你手动画图会发现一颗子树下的序列就是一个连续序列,序列起点tii,序列终点tio,,所以待会线段树修改树上一个点时,实际上就是一个区间。。。。。(好神奇,手动鼓掌),,而且,之前一直用树链剖分去套,,,写了两个dfs,然而这道题完全没必要,树链剖分写的另一个dfs,是修改一段区间时,他的tii并不是连续的,所以会有一个函数来切割区间,分成几段连续区间,进行计算。。。。
但是(终于到但是了。。)这个是只修改树上的节点,而树上的节点所包含的实际就是一个连续区间。。。。
、、、、、、、、分割。。,,,,,,,,,,,,,
关于线段树部分
当加一个k时,他的子树所加的是depth[v]-depth[u]+k
所以把这个值当成两部分,depth[v],和k-depth[u]
sum_tot(求depth[v]部分):假设这个节点一共操作了tot次,那么这个节点的(树上)子树,,的
sum_tot+=tot*sigma(depth[v])
sum_delta(就是第二部分):sum_delta=size*sigam(k-depth[u])
注意,这里的size 是线段树节点所包含的节点总数,与树上节点不同,,(总之就是忘掉树吧),,还有就是,我之前一直很纠结为什么是sigma(k-depth[u]),,,这里的sigma的含义是tot次操作的sigma,每次操作的k值不同,。。。。
所以这两部分就用线段树来维护………

#include<iostream>
#include<cstdio>
#include<ctime>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<string>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
#define INF 0x3f3f3f3f
#define clock CLOCKS_PER_SEC
#define cle(x) memset(x,0,sizeof(x))
#define maxcle(x) memset(x,127,sizeof(x))
#define mincle(x) memset(x,-1,sizeof(x))
#define minn(x1,x2,x3) min(x1,min(x2,x3))
#define cop(a,x) memcpy(x,a,sizeof(a))
#define FROP "c"
#define C(a,b) next_permutation(a,b)
#define LL long long
#define smin(x,tmp) x=min(x,tmp)
#define smax(x,tmp) x=max(x,tmp)
using namespace std;
const int N=50005;
struct Edge
{
    int to,ne;
    Edge(int to=0,int ne=0):to(to),ne(ne){ }
}ed[N];
int head[N],fa[N],son[N],tii[N],_tii[N],tio[N],depth[N],siz[N],Index,top[N];
void dfs1(int u)
{
    siz[u]=1;
    tii[u]=++Index;
    _tii[Index]=u;
    for(int i = head[u]; i ; i =ed[i].ne)
    {
        int v=ed[i].to;
        if(fa[u]==v)continue;
        fa[v]=u;
        depth[v]=depth[u]+1;
        dfs1(v);
        if(siz[v]>siz[son[u]])
            son[u]=v;
        siz[u]+=siz[v];
    }
    tio[u]=Index;
}
//---------------------------------------------------------
struct Node
{
    LL dep,tot,sum_tot;
    LL size,delta,sum_delta;
}node[N<<2];
#define dep(x) node[x].dep
#define tot(x) node[x].tot
#define sum_tot(x) node[x].sum_tot
#define size(x) node[x].size
#define delta(x) node[x].delta
#define sum_delta(x) node[x].sum_delta
void add_tot(int rt,int val)
{
    tot(rt) += val;
    sum_tot(rt) += dep(rt) * val;
}
void add_delta(int rt,int val)
{
    delta(rt) += val;
    sum_delta(rt) += size(rt) * val;
}
void pushup_tot(int rt){sum_tot(rt)=sum_tot(rt<<1)+sum_tot(rt<<1|1);}
void pushup_delta(int rt){sum_delta(rt)=sum_delta(rt<<1)+sum_delta(rt<<1|1);}
void pushdown_tot(int rt)
{
    if(!tot(rt))return;
    add_tot(rt<<1,tot(rt));
    add_tot(rt<<1|1,tot(rt));
    tot(rt)=0;
}
void pushdown_delta(int rt)
{
    if(!delta(rt))return;
    add_delta(rt<<1,delta(rt));
    add_delta(rt<<1|1,delta(rt));
    delta(rt)=0;
}
#define lson rt<<1,l,mid
#define rson rt<<1|1,mid+1,r
void modify_tot(int rt,int l,int r,int L,int R,int val)
{
    if(l>=L && R>=r)
    {
        add_tot(rt,val);
        return;
    }
    pushdown_tot(rt);
    int mid=(l+r)>>1;
    if(mid>=L)modify_tot(lson,L,R,val);
    if(mid<R)modify_tot(rson,L,R,val);
    pushup_tot(rt);
}
void modify_delta(int rt,int l,int r,int L,int R,int val)
{
    if(l>=L && R>=r)
    {
        add_delta(rt,val);
        return;
    }
    pushdown_delta(rt);
    int mid=(l+r)>>1;
    if(mid>=L)modify_delta(lson,L,R,val);
    if(mid<R)modify_delta(rson,L,R,val);
    pushup_delta(rt);
}
LL query(int rt,int l,int r,int L,int R)
{
    if(l>=L && R>=r)return sum_tot(rt)+sum_delta(rt);
    pushdown_tot(rt);
    pushdown_delta(rt);
    int mid=(l+r)>>1;
    LL res=0;
    if(mid>=L)res+=query(lson,L,R);
    if(mid<R)res+=query(rson,L,R);
    return res;
}
void pushup(int rt)
{
    dep(rt)=dep(rt<<1)+dep(rt<<1|1);
    size(rt)=size(rt<<1)+size(rt<<1|1);
}
void build(int rt,int l,int r)
{
    if(l==r)
    {
        dep(rt)=depth[_tii[l]];
        size(rt)=1;//就是线段树size,不是树上size
        return;
    }
    int mid=(l+r)>>1;
    build(lson);
    build(rson);
    pushup(rt);
}
//---------------------------------------------------------------------------------------------------
int n,m;
void init()
{
    scanf("%d%d",&n,&m);
    for(int i = 1; i < n; i++)
    {
        int x;
        scanf("%d",&x);
        ed[i]=Edge(i+1,head[x]);
        head[x]=i;
    }
    depth[1]=1;dfs1(1);
    build(1,1,n);
}
char ch[5];
void work()
{
    while(m--)
    {
        int x;
        scanf("%s",ch);
        scanf("%d",&x);
        if(ch[0]=='A')
        {
            int w;
            scanf("%d",&w);
            modify_tot(1,1,n,tii[x],tio[x],1);
            modify_delta(1,1,n,tii[x],tio[x],w-depth[x]);
        }
        else
        {
            LL ans=query(1,1,n,tii[x],tio[x]);
            printf(AUTO "\n",ans);
        }
    }
}
//-------------------------------------------------------------------------------------------
int main()
{
    freopen(FROP".in","r",stdin);
    freopen(FROP".out","w",stdout);
    init();
    work();
    return 0;
}

T3解法原链接,何神!!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值