[NOIP模拟题][杂题][状压DP][DFS序][线段树]

T1

给定一个只含’B”R’的字符串,len<=100,但这个字符串可以无限延伸,即BRBR可以延伸为BRBRBRBRBRBRBR,给定一个区间[L,R],求区间内B的总数,L,R<=10^18

取模之后随便搞吧

#include<cstdio>
#include<cstring>
#include<algorithm>
#ifdef WIN32 
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif 
#define LL long long 
using namespace std;
const int maxn=100+5;
LL L,R,len1,len2,temp1,temp2,temp3,ans;
char ch[maxn];
int main()
{
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    scanf("%s" AUTO AUTO,ch+1,&L,&R);
    len1=strlen(ch+1); len2=R-L+1;
    temp1=L%len1; if (!temp1) temp1=len1; 
    temp2=R%len1; if (!temp2) temp2=len1;
    temp3=len2/len1;
    for (int i=1;i<=len1;i++) if (ch[i]=='B') ans++; ans*=temp3;
    if ((len2%len1)==0)
    {
        printf(AUTO,ans);
        return 0;
    }
    if (temp1<=temp2)
    {
        for (int i=temp1;i<=temp2;i++) if (ch[i]=='B') ans++;
    } 
    else
    {
        for (int i=temp1;i<=len1;i++) if (ch[i]=='B') ans++;
        for (int i=1;i<=temp2;i++) if (ch[i]=='B') ans++;
    }
    printf(AUTO,ans);
    return 0;
}

T2

给定n种食物,有美味值w[i],有k种加成关系,对于三元组(x,y,z)表示吃完x后马上吃y后能够获得额外的z的价值,要求吃m个食物,求能得到的最大美味值,n<=18

18的数据量就因该想到状压DP了
不过这道题有个吃的顺序问题,所以还需再加一维来表示这种状态最后一个吃的,f[S][i]即为吃的为S状态,最后一个吃的为i,那么用刷表法就可以很好的更新S能够转移出来的状态了
然而这么一道状压水题,我还是没有做出来,虽然方程表示是一样的,但是我一直想着如何用填表发来做,MDZZ,状压就该多想去刷表啊

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else 
#define AUTO "%lld"
#endif
#define LL long long
using namespace std;
const int maxn=18,maxm=(1<<maxn);
int n,m,k,w[maxn],value[maxn][maxn];
LL dp[maxm][maxn],ans;
int readint()
{
    int x=0; char ch=getchar();
    while (!isdigit(ch)) ch=getchar();
    while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0'; ch=getchar();}
    return x;
}
int cal(int S)
{
    int ret=0;
    for (int i=0;i<n;i++) if (S&(1<<i)) ret++;
    return ret; 
}
int main()
{
    freopen("b.in","r",stdin);
    freopen("b.out","w",stdout);
    n=readint(); m=readint(); k=readint();
    for (int i=0;i<n;i++) w[i]=readint();
    for (int i=0;i<n;i++) dp[1<<i][i]=w[i];
    for (int i=1;i<=k;i++)
    {
        int u=readint(),v=readint();
        value[u-1][v-1]=readint();
    }
    for (int S=0;S<(1<<n);S++)
    {
        int cnt=cal(S);
        if (cnt==m)
        {
            for (int i=0;i<n;i++) if (S&(1<<i)) 
              ans=max(ans,dp[S][i]);
        }
        else if (cnt>m) continue;
        else
        {
            for (int i=0;i<n;i++) if (!(S&(1<<i)))
              for (int j=0;j<n;j++) if (S&(1<<j))
                dp[S|(1<<i)][i]=max(dp[S|(1<<i)][i],dp[S][j]+w[i]+value[j][i]);
        }
    }
    printf(AUTO,ans);
    return 0;  
}

T3

给定一棵树,1,为根节点,有m个操作或询问,操作(x,k)为将x节点权值加k,它儿子加k+1,孙子加k+2,以此类推,查询为求x及它形成子树权值总和
n<=5e4,m<=1e5

我们先考虑查询的一般化公式,对于一次操作(u,k)那么这个子树上任意节点v产生的贡献为,k+dep[v]-dep[u],将其分为两部分①dep[v]②k-dep[u] 很明显一个子树上②都是相等的,那么这次操作后整个子树产生的贡献为sigma(dep[v]|v属于这个子树)+(k-dep[u])*size
然后看到子树就应该想到dfs序,将一整个子树转化成一条连续的线段,一棵子树实质上为一条连续的线段,然后线段树来维护就好了
似乎说的不是很清楚?

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
#ifdef WIN32
#define AUTO "%I64d"
#else 
#define AUTO "%lld"
#endif 
#define LL long long 
using namespace std;
const int maxn=5e4+5,maxm=1e5+5;
int n,m,edge,cnt,depth[maxn],pre[maxn],rank[maxn],post[maxn],to[maxn],nxt[maxn],head[maxn];
LL ans;
struct Seg
{
    LL sum1,sum2,delta,dep,size,tot;
}seg[maxn<<2];
#define Lson(x) (x<<1)
#define Rson(x) (x<<1|1)
int readint()
{
    int x=0; char ch=getchar();
    while (!isdigit(ch)) ch=getchar();
    while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0'; ch=getchar();}
    return x;
}
char readchar()
{
    char ch=getchar();
    while (ch!='A'&&ch!='Q') ch=getchar();
    return ch;
}
void edge_add(int u,int v)
{
    to[++edge]=v; nxt[edge]=head[u]; head[u]=edge; 
}
void dfs(int u,int d)
{
    pre[u]=++cnt; depth[u]=d; rank[cnt]=u;
    for (int E=head[u];E;E=nxt[E])
    {
        int v=to[E]; dfs(v,d+1);
    }
    post[u]=cnt;
}
void pushup(int tr)
{
    seg[tr].size=seg[Lson(tr)].size+seg[Rson(tr)].size;
    seg[tr].dep=seg[Lson(tr)].dep+seg[Rson(tr)].dep;
}
void build(int L,int R,int tr)
{
    if (L==R) 
    {
        seg[tr].dep=depth[rank[L]]; seg[tr].size=1; return ;
    }
    int mid=(L+R)>>1;
    build(L,mid,Lson(tr)); build(mid+1,R,Rson(tr));
    pushup(tr);
}
void add1(int tr,int val)
{
    seg[tr].tot+=val; seg[tr].sum1+=seg[tr].dep*val;
}
void add2(int tr,int val)
{
    seg[tr].delta+=val; seg[tr].sum2+=seg[tr].size*val;
}
void pushup1(int tr)
{
    seg[tr].sum1=seg[Lson(tr)].sum1+seg[Rson(tr)].sum1;
}
void pushup2(int tr)
{
    seg[tr].sum2=seg[Lson(tr)].sum2+seg[Rson(tr)].sum2;
}
void pushdown1(int tr)
{
    if(!seg[tr].tot) return;
    add1(Lson(tr),seg[tr].tot); add1(Rson(tr),seg[tr].tot);
    seg[tr].tot=0;
}
void pushdown2(int tr)
{
    if(!seg[tr].delta) return;
    add2(Lson(tr),seg[tr].delta); add2(Rson(tr),seg[tr].delta);
    seg[tr].delta=0;
}
void update1(int L,int R,int tr,int u,int v,int val)
{
    if (L==u&&v==R)
    {
        add1(tr,val); return ;
    }
    pushdown1(tr); int mid=(L+R)>>1;
    if (v<=mid) update1(L,mid,Lson(tr),u,v,val);
    else if (u>=mid+1) update1(mid+1,R,Rson(tr),u,v,val);
    else update1(L,mid,Lson(tr),u,mid,val),update1(mid+1,R,Rson(tr),mid+1,v,val);
    pushup1(tr);
}
void update2(int L,int R,int tr,int u,int v,int val)
{
    if (L==u&&v==R)
    {
        add2(tr,val); return ;
    }
    pushdown2(tr); int mid=(L+R)>>1;
    if (v<=mid) update2(L,mid,Lson(tr),u,v,val);
    else if (u>=mid+1) update2(mid+1,R,Rson(tr),u,v,val);
    else update2(L,mid,Lson(tr),u,mid,val),update2(mid+1,R,Rson(tr),mid+1,v,val);
    pushup2(tr);
}
LL query(int L,int R,int tr,int u,int v)
{
    if (u==L&&R==v) return seg[tr].sum1+seg[tr].sum2;
    pushdown1(tr); pushdown2(tr); int mid=(L+R)>>1;
    if (v<=mid) return query(L,mid,Lson(tr),u,v);
    if (u>=mid+1) return query(mid+1,R,Rson(tr),u,v);
    return query(L,mid,Lson(tr),u,mid)+query(mid+1,R,Rson(tr),mid+1,v);
}
int main()
{
    freopen("c.in","r",stdin);
    freopen("c.out","w",stdout);
    n=readint(); m=readint();
    for (int i=2;i<=n;i++)
    {
        int fa=readint(); edge_add(fa,i);
    }
    dfs(1,1); build(1,n,1);
    while (m--)
    {
        char op=readchar(); int x=readint();
        switch (op)
        {
            case 'Q':ans=query(1,n,1,pre[x],post[x]); printf(AUTO,ans); putchar('\n'); break;
            case 'A':int k=readint(); update1(1,n,1,pre[x],post[x],1); update2(1,n,1,pre[x],post[x],k-depth[x]);
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值