烟火

城镇的主干道上有n个区域,从左到右编号为1到n,每个区域之间相距1个单位距离。在节日中要放m个烟火,第i个烟火会在ti时刻的ai区域放。如果在ti时刻你所处区域为x,那么你可以获得bi - | ai - x |的快乐值。在每个单位时间你可以移动不超过d个单位距离,初始的位置是任意的,求通过移动能获得快乐值和的最大值。

1 <= n <= 150000,1 <= m <= 300,1 <= d, ai <= n,1 <= bi, ti <= 10^9,对于i >= 2有ti >= ti-1。

题解

状态设计很简单f[i][j]表示看了i个烟火,在j的位置的最大快乐值。

f[i][j]=max{f[i-1][k]}+b[i]-|a[i]-j|

k是一段连续的区间,并且对于同一个i长度固定,所以可以用单调队列维护,注意可以双向移动。

注意pos开long long

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define ll long long
const int maxn=150005;
const ll inf=1000000000000ll;
int n,m;
ll d;
ll f[2][maxn];
int a[maxn],b[maxn],T[maxn];
int h,t,q[maxn];

template<class T>inline void read(T &x){
    x=0;int f=0;char ch=getchar();
    while(!isdigit(ch)) {f|=(ch=='-');ch=getchar();}
    while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    x = f ? -x : x ;
}

ll abs(ll x){return x<0 ? -x : x ;}
ll min(ll x,ll y){return x<y ? x : y ;}

int main(){
    freopen("fireworks.in","r",stdin);
    freopen("fireworks.out","w",stdout);
    read(n);read(m);read(d);
    for(int i=1;i<=m;i++) read(a[i]),read(b[i]),read(T[i]);
    for(int i=1;i<=n;i++) f[1][i]=b[1]-abs(a[1]-i);
    for(int i=2;i<=m;i++){
        h=1,t=0;
        int now=i&1,pre=now^1;
        for(int j=1;j<=min(n,1+d*(T[i]-T[i-1]));j++){
            while(h<=t&&f[pre][j]>f[pre][q[t]]) t--;
            q[++t]=j;
        }
        for(int j=1;j<=n;j++){
            while(h<=t&&abs(j-q[h])>d*(T[i]-T[i-1])) h++;
            int p=q[h];
            f[now][j]=f[pre][p]+b[i]-abs(a[i]-j);
            ll pos=j+1+(ll)d*(T[i]-T[i-1]);
            if(pos<=n){
                while(h<=t&&f[pre][pos]>f[pre][q[t]]) t--;
              q[++t]=pos;
            }
        }
    }
    ll mx=-inf;
    for(int i=1;i<=n;i++) mx=max(mx,f[m&1][i]);
    printf("%lld",mx);
}
/*
10 2 1
1 1000 2
9 1000 4
*/
fireworks

购物

商店里有n个物品,第i个物品的价格为ci元。每个物品只能买一次。商店发行了n张优惠券,每个物品各有一张优惠卷。如果使用了第i张优惠券,可以使该物品便宜di元钱,必须买商品才能够使用相对应的优惠券。第1张优惠卷可以无条件使用,但对于第i>=2张优惠卷,如果需要使用第i张优惠券,则必须先使用xi这张优惠券。
现在有b元钱,问最多能购买多少商品。

1 <= n <= 5000,1 <= b <= 10^9,1 <= di < ci <= 10^9,对于i >= 2有1 <= xi < i。

题解

显然商品构成了树形结构。

f[i][j][0/1]表示i的子树中选取j个商品,不使用/使用i的优惠券所花的最少费用。

最会找最多的商品并且费用<=b

转移比较好想,但是因为这道题卡空间,但是因为b<=1e9,所以大于1e9的都不用考虑。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define ll long long
const int maxn=5005;
const ll oo=1000000001;
int n,m;
int c[maxn],d[maxn];
int f[maxn][maxn][2];
int size[maxn];
int cnt,head[maxn];
struct edge{
    int x,y,next;
}e[maxn];

void add(int x,int y){
    e[++cnt]=(edge){x,y,head[x]};
    head[x]=cnt;
}

template<class T>inline void read(T &x){
    x=0;int f=0;char ch=getchar();
    while(!isdigit(ch)) {f|=(ch=='-');ch=getchar();}
    while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    x = f ? -x : x ;
}

ll min(ll x,ll y){return x<y ? x : y ;}

void dfs(int x){
    size[x]=1;
    f[x][1][0]=c[x];
    f[x][1][1]=c[x]-d[x];
    f[x][0][0]=0;
    for(int i=head[x];i;i=e[i].next){
        int y=e[i].y;
        dfs(y);
        for(int j=size[x];~j;j--)
         for(int k=1;k<=size[y];k++){
              f[x][j+k][0]=min(f[x][j+k][0],f[x][j][0]+f[y][k][0]);
              f[x][j+k][1]=min(f[x][j+k][1],min(f[y][k][0],f[y][k][1])+f[x][j][1]);
         }
        size[x]+=size[y];
    }
}

int main(){
    freopen("shopping.in","r",stdin);
    freopen("shopping.out","w",stdout);
    read(n);read(m);
    read(c[1]);read(d[1]);
    for(int i=2;i<=n;i++){
        read(c[i]);read(d[i]);
        int x;read(x);
        add(x,i);
    }
    memset(f,0x3f,sizeof(f));
    cnt=0;
    dfs(1);
    for(int i=n;~i;i--) if(min(f[1][i][1],f[1][i][0])<=m) {printf("%d",i);return 0;}
}
/*
8 9
4 3
8 3 1
2 1 1
4 2 2
7 2 2
3 1 2
7 3 5
2 1 3
*/
shopping

括号匹配

给出长度为N的括号序列(只包含(,),[,]),问有多少种方法删掉这些括号的一个子集,使得剩下的括号序列是合法的,请注意不能全部删完。

100%的数据保证:1 <= N <= 300。

题解

考虑在删除一对合法的括号之后,剩下的两个部分的方案数相乘为总方案数(乘法原理)。

对于当前区间,最左边的可以匹配或者不匹配,递归搜索即可。

记忆化搜索

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define ll long long
const int mod=1000000007;
const int maxn=305;
int n;
ll f[maxn][maxn][2];//[l,r]是否出现了合法序列 
char s[maxn];

bool cx(int l,int r){return (s[l]=='('&&s[r]==')')||(s[l]=='['&&s[r]==']');
}

ll dfs(int l,int r,bool opt){
    if(f[l][r][opt]!=-1) return f[l][r][opt];
    if(l>=r) return opt;
    ll ret=dfs(l+1,r,opt)%mod;//不进行匹配 
    if(s[l]=='('||s[l]=='['){
        for(int i=l;i<=r;i++)
       if(cx(l,i))
        ret=(ret+dfs(l+1,i-1,1)*dfs(i+1,r,1)%mod)%mod;
    }
    return f[l][r][opt]=ret;
}

int main(){
    freopen("parenthesis.in","r",stdin);
    freopen("parenthesis.out","w",stdout);
    scanf("%d%s",&n,s+1);
    memset(f,-1,sizeof(f));
    printf("%lld",dfs(1,n,0));
}
parenthesis
02-13 06:30