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

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<cstring>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<iomanip>
#include<ctime>
#include<cctype>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
#define smax(x,tmp) x=max((x),(tmp))
#define smin(x,tmp) x=min((x),(tmp))
#define maxx(x1,x2,x3) max(max(x1,x2),x3)
#define minn(x1,x2,x3) min(min(x1,x2),x3)
typedef long long LL;
template <class T> inline void read(T &x)
{
    x = 0;
    T flag = 1;
    char ch = (char)getchar();
    while(ch<'0' || ch>'9')
    {
        if(ch == '-') flag = -1;
        ch = (char)getchar();
    }
    while(ch>='0' && ch<='9')
    {
        x = (x<<1) + (x<<3) + ch - '0';
        ch = (char)getchar();
    }
    x *= flag;
}
template <class T> T gcd(T a,T b) { return !b?a:gcd(b,a%b); }
const int INF=0x3f3f3f3f;
const int maxn = 105;
char s[maxn];
int sum[maxn];
LL L,R;
int n;
LL calc(LL x)
{
    LL ret = sum[n] * (x/n);
    ret += sum[x%n];
    return ret;
}
int main()
{
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    scanf("%s"AUTO AUTO,s+1,&L,&R);
    n = strlen(s+1);
    for(int i=1;i<=n;i++) sum[i] = sum[i-1] + (s[i]=='B');
    LL ans = calc(R) - calc(L-1);
    printf(AUTO,ans);
    return 0;
}

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(S,i)表示已经选了的集合为S,最后一个选的是i的最大价值。
方程 :dp[S+{i}][i] = dp[S][j] + a[i] + g[j][i] , 其中 j 属于 S ,i 不属于S
每次如果S中1的个数为m,就更新答案。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<iomanip>
#include<ctime>
#include<cctype>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
#define smax(x,tmp) x=max((x),(tmp))
#define smin(x,tmp) x=min((x),(tmp))
#define maxx(x1,x2,x3) max(max(x1,x2),x3)
#define minn(x1,x2,x3) min(min(x1,x2),x3)
typedef long long LL;
template <class T> inline void read(T &x)
{
    x = 0;
    T flag = 1;
    char ch = (char)getchar();
    while(ch<'0' || ch>'9')
    {
        if(ch == '-') flag = -1;
        ch = (char)getchar();
    }
    while(ch>='0' && ch<='9')
    {
        x = (x<<1) + (x<<3) + ch - '0';
        ch = (char)getchar();
    }
    x *= flag;
}
template <class T> T gcd(T a,T b) { return !b?a:gcd(b,a%b); }
const int maxS = (1<<18) + 5;
const int maxn = 20;
int a[maxn];
int g[maxn][maxn];
LL dp[maxS][maxn];
int n,m,k;
inline void init()
{
    read(n); read(m); read(k);
    for(int i=0;i<n;i++) read(a[i]);
    for(int i=1;i<=k;i++)
    {
        int x,y,c;
        read(x); read(y); read(c);
        x--; y--;
        g[x][y] += c;
    }
}
LL dynamic()
{
    LL ans = 0;
    for(int i=0;i<n;i++) dp[(1<<i)][i] = a[i];
    for(int S=1;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]);
                else continue;
        else
            for(int i=0;i<n;i++) if(!(S&(1<<i))) // haven't been eaten
                for(int j=0;j<n;j++) if(S&(1<<j)) // the last one
                    smax(dp[S|(1<<i)][i],dp[S][j]+a[i]+g[j][i]);
    return ans;
}
int main()
{
    freopen("b.in","r",stdin);
    freopen("b.out","w",stdout);
    init();
    LL ans = dynamic();
    printf(AUTO,ans);
    return 0;
}

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%:直接在节点做标记,每次查询时下传即可。考虑到树的形状不是平衡的,直接这样做肯定要T

100%:
每次操作都是对一个子树的操作,很容易想到在dfs序中区间修改和标记下传,这样就可以保证nlogn的时间复杂度了。
但是每个子树的修改时不一样的,新操作一次的贡献是k+depth[v]-depth[u],由于depth[v]是节点本身的性质,而k-depth[u]是这次操作的性质,那么可以考虑分成两部分来求解。
假设某个节点被操作了tot次(包括它的父亲的操作),那么这部分的贡献是tot*depth[v],对于一个子树来说,这部分总的贡献就是tot*Sigma(depth[v])。
再来处理操作的贡献。每一个节点都是k-depth[u],那么子树的贡献就是size*Sigma(k-depth[u])
这两部分分别用线段树来维护即可。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<iomanip>
#include<ctime>
#include<cctype>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
#define smax(x,tmp) x=max((x),(tmp))
#define smin(x,tmp) x=min((x),(tmp))
#define maxx(x1,x2,x3) max(max(x1,x2),x3)
#define minn(x1,x2,x3) min(min(x1,x2),x3)
typedef long long LL;
template <class T> inline void read(T &x)
{
    x = 0;
    T flag = 1;
    char ch = (char)getchar();
    while(ch<'0' || ch>'9')
    {
        if(ch == '-') flag = -1;
        ch = (char)getchar();
    }
    while(ch>='0' && ch<='9')
    {
        x = (x<<1) + (x<<3) + ch - '0';
        ch = (char)getchar();
    }
    x *= flag;
}
template <class T> T gcd(T a,T b) { return !b?a:gcd(b,a%b); }
//end template

const int INF=0x3f3f3f3f;
const int maxn = 50005;

// original tree related
struct Edge
{
    int to,next;
}edge[maxn<<1];
int head[maxn];
int maxedge;
inline void addedge(int u,int v)
{
    edge[++maxedge] = (Edge) { v,head[u] };
    head[u] = maxedge;
    edge[++maxedge] = (Edge) { u,head[v] };
    head[v] = maxedge;
}
int maxnode;
int id[maxn]; // from dfn to original node id
int pre[maxn],post[maxn]; // two dfs_clocks
int depth[maxn];
void dfs(int u,int father,int deep)
{
    depth[u] = deep;
    pre[u] = ++maxnode;
    id[maxnode] = u;
    for(int i=head[u];~i;i=edge[i].next)
    {
        int v = edge[i].to;
        if(v == father) continue;
        dfs(v,u,deep+1);
    }
    post[u] = maxnode;
}
// end original tree

// segment tree related
struct Node
{
    LL dep,tot,sum_tot; // sum of depth[v] , total add (lazy) , sum part 1
    LL delta,size,sum_delta; // sum of delta , total nodes (lazy) , sum part 2
}node[maxn<<2];
#define dep(x) node[x].dep
#define tot(x) node[x].tot
#define sum_tot(x) node[x].sum_tot
#define delta(x) node[x].delta
#define size(x) node[x].size
#define sum_delta(x) node[x].sum_delta
inline void add_tot(int root,LL val)
{
    tot(root) += val;
    sum_tot(root) += val * dep(root);
}
inline void add_delta(int root,LL val)
{
    delta(root) += val;
    sum_delta(root) += val * size(root);
}
inline void pushdown_tot(int root)
{
    if(!tot(root)) return;
    add_tot(root<<1,tot(root));
    add_tot(root<<1|1,tot(root));
    tot(root) = 0;
}
inline void pushdown_delta(int root)
{
    if(!delta(root)) return;
    add_delta(root<<1,delta(root));
    add_delta(root<<1|1,delta(root));
    delta(root) = 0;
}
inline void update_tot(int root) { sum_tot(root) = sum_tot(root<<1) + sum_tot(root<<1|1); }
inline void update_delta(int root) { sum_delta(root) = sum_delta(root<<1) + sum_delta(root<<1|1); }
void modify_tot(int root,int l,int r,int x,int y,LL val)
{
    if(x<=l && r<=y)
    {
        add_tot(root,val);
        return;
    }
    pushdown_tot(root);
    int mid = (l+r)>>1;
    if(x<=mid && l<=y) modify_tot(root<<1,l,mid,x,y,val);
    if(y>=mid+1 && r>=x) modify_tot(root<<1|1,mid+1,r,x,y,val);
    update_tot(root);
}
void modify_delta(int root,int l,int r,int x,int y,LL val)
{
    if(x<=l && r<=y)
    {
        add_delta(root,val);
        return;
    }
    pushdown_delta(root);
    int mid = (l+r)>>1;
    if(x<=mid && l<=y) modify_delta(root<<1,l,mid,x,y,val);
    if(y>=mid+1 && r>=x) modify_delta(root<<1|1,mid+1,r,x,y,val);
    update_delta(root);
}
LL query(int root,int l,int r,int x,int y)
{
    if(x<=l && r<=y) return sum_tot(root)+sum_delta(root);
    pushdown_tot(root); pushdown_delta(root);
    int mid = (l+r)>>1;
    LL ret = 0;
    if(x<=mid && l<=y) ret += query(root<<1,l,mid,x,y);
    if(y>=mid+1 && r>=x) ret += query(root<<1|1,mid+1,r,x,y);
    return ret;
}
inline void update(int root)
{
    dep(root) = dep(root<<1) + dep(root<<1|1);
    size(root) = size(root<<1) + size(root<<1|1);
}
void build(int root,int l,int r)
{
    if(l == r)
    {
        dep(root) = depth[id[l]];
        size(root) = 1;
        return;
    }
    int mid = (l+r)>>1;
    build(root<<1,l,mid);
    build(root<<1|1,mid+1,r);
    update(root);
}
// end segment tree

// main
int n,q,Root;
inline void init()
{
    memset(head,-1,sizeof(head)); maxedge=-1;
    read(n); read(q); Root=1;
    for(int i=2;i<=n;i++)
    {
        int to;
        read(to);
        addedge(i,to);
    }
    dfs(Root,-1,1);
    build(1,1,n);
}
void work()
{
    for(int i=1;i<=q;i++)
    {
        char ch = (char) getchar();
        while(!isalpha(ch)) ch = (char) getchar();
        int x,k;
        read(x);
        if(ch == 'Q')
        {
            LL ans = query(1,1,n,pre[x],post[x]);
            printf(AUTO,ans);
            putchar('\n');
        }
        else
        {
            read(k);
            modify_tot(1,1,n,pre[x],post[x],1);
            modify_delta(1,1,n,pre[x],post[x],k-depth[x]);
        }
    }
}
int main()
{
    freopen("c.in","r",stdin);
    freopen("c.out","w",stdout);
    init();
    work();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值