Atcoder Educational DP Contest

前面简单一点的题直接过吧。

A 暴力DP

B 怎么还是暴力DP

C 还是暴力DP

D 直接背包

E 这个背包不太一样了,这里有一个技巧,就是因为价值很小,所以直接对价值背包,求出来达到某一个权值最小的重量,然后找到满足限制的最大的价值即可。注意,如果能达到权值比这个还大的点,那么这个点很显然也是可以达到的。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cctype>
#define qmin(x,y) (x=min(x,y))
#define qmax(x,y) (x=max(x,y))
using namespace std;

inline char gc() {
//  static char buf[100000],*p1,*p2;
//  return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
    return getchar();
}

template<class T>
int read(T &ans) {
    ans=0;char ch=gc();T f=1;
    while(!isdigit(ch)) {
        if(ch==EOF) return -1;
        if(ch=='-') f=-1;
        ch=gc();
    }
    while(isdigit(ch))
        ans=ans*10+ch-'0',ch=gc();
    ans*=f;return 1;
}

template<class T1,class T2>
int read(T1 &a,T2 &b) {
    return read(a)!=EOF&&read(b)!=EOF?2:EOF;
}

template<class T1,class T2,class T3>
int read(T1 &a,T2 &b,T3 &c) {
    return read(a,b)!=EOF&&read(c)!=EOF?3:EOF;
}

typedef long long ll;
const int Maxn=210000;
const int inf=0x3f3f3f3f;

int n,w,tot;
ll f[Maxn],v;

signed main() {
//  freopen("test.in","r",stdin);
    read(n,tot);
    memset(f,0x3f,sizeof(f));f[0]=0;
    int now=0;
    for(int i=1;i<=n;i++) {
        read(w,v);
        for(int j=now+v;j>=v;j--) f[j]=min(f[j],f[j-v]+w);
        now+=v;
    }
    for(int i=now;i>=0;i--) f[i]=min(f[i],f[i+1]);
    int ans=0;
    while(f[ans]<=tot) ans++;
    printf("%d\n",ans-1);
    return 0;
}

F 套路题,统计答案略恶心,还是贴一下代码吧。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cctype>
#define qmin(x,y) (x=min(x,y))
#define qmax(x,y) (x=max(x,y))
using namespace std;

inline char gc() {
//  static char buf[100000],*p1,*p2;
//  return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
    return getchar();
}

template<class T>
int read(T &ans) {
    ans=0;char ch=gc();T f=1;
    while(!isdigit(ch)) {
        if(ch==EOF) return -1;
        if(ch=='-') f=-1;
        ch=gc();
    }
    while(isdigit(ch))
        ans=ans*10+ch-'0',ch=gc();
    ans*=f;return 1;
}

template<class T1,class T2>
int read(T1 &a,T2 &b) {
    return read(a)!=EOF&&read(b)!=EOF?2:EOF;
}

template<class T1,class T2,class T3>
int read(T1 &a,T2 &b,T3 &c) {
    return read(a,b)!=EOF&&read(c)!=EOF?3:EOF;
}

typedef long long ll;
const int Maxn=3100;
const int inf=0x3f3f3f3f;

int f[Maxn][Maxn],pre[Maxn][Maxn],top;
char a[Maxn],s[Maxn],st[Maxn];

signed main() {
//  freopen("test.in","r",stdin);
    scanf("%s%s",a,s);
    int n=strlen(a),m=strlen(s);
    for(int i=0;i<m;i++) if(a[0]==s[i]) f[0][i]=1;
    for(int i=0;i<m;i++) pre[0][i]=-1;
    for(int i=1;i<n;i++) {
        int now=0,las=-1;
        for(int j=0;j<m;j++) {
            if(a[i]==s[j]) {
                if(f[i-1][j]>now+1) {
                    now=f[i-1][j],las=(a[i-1]==s[j]?i-1:pre[i-1][j]);
                    pre[i][j]=las;
                    f[i][j]=now;
                }
                else {
                    pre[i][j]=las;
                    f[i][j]=now+1;
                    if(f[i-1][j]>now)
                        now=f[i-1][j],las=(a[i-1]==s[j]?i-1:pre[i-1][j]);
                }
            }
            else {
                if(f[i-1][j]>now)
                    now=f[i-1][j],las=(a[i-1]==s[j]?i-1:pre[i-1][j]);
                pre[i][j]=las;
                f[i][j]=now;
            }
        }
    }
    int ans=0,temp;
    for(int i=0;i<m;i++) if(f[n-1][i]>ans) {
        ans=f[n-1][i];
        temp=i;
    }
    if(ans==0) return 0;
    int nx,ny=temp;
    if(a[n-1]==s[temp]) nx=n-1;
    else nx=pre[n-1][temp];
    while(nx!=-1) {
        st[++top]=a[nx];
        nx=pre[nx][ny];
        int ans=0,temp;
        for(int i=0;i<ny;i++)
            if(f[nx][i]>ans) ans=f[nx][i],temp=i;
        ny=temp;
    }
    while(top) putchar(st[top--]);
    return 0;
}

G 直接DAG上的DP,太简单了不放代码了。

H 每个点都是从上边或左边转移即可。

I 概率DP,直接记有i个正面朝上的概率,然后就可以\(O(n^2)\)DP了。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cctype>
#define qmin(x,y) (x=min(x,y))
#define qmax(x,y) (x=max(x,y))
using namespace std;

inline char gc() {
//  static char buf[100000],*p1,*p2;
//  return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
    return getchar();
}

template<class T>
int read(T &ans) {
    ans=0;char ch=gc();T f=1;
    while(!isdigit(ch)) {
        if(ch==EOF) return -1;
        if(ch=='-') f=-1;
        ch=gc();
    }
    while(isdigit(ch))
        ans=ans*10+ch-'0',ch=gc();
    ans*=f;return 1;
}

template<class T1,class T2>
int read(T1 &a,T2 &b) {
    return read(a)!=EOF&&read(b)!=EOF?2:EOF;
}

template<class T1,class T2,class T3>
int read(T1 &a,T2 &b,T3 &c) {
    return read(a,b)!=EOF&&read(c)!=EOF?3:EOF;
}

typedef long long ll;
const int Maxn=11000;
const int inf=0x3f3f3f3f;
const int mod=1000000007;

int n;
double f[Maxn],p;

signed main() {
//  freopen("test.in","r",stdin);
    read(n);
    f[0]=1;
    for(int i=1;i<=n;i++) {
        scanf("%lf",&p);
        for(int j=i;j>=1;j--) f[j]=(f[j]*(1.0-p)+f[j-1]*p);
        f[0]*=1.0-p;
    }
    double ans=0;
    for(int i=n/2+1;i<=n;i++) ans+=f[i];
    printf("%.10lf",ans);
    return 0;
}

J 期望DP,因为每个数都很小,直接记目前剩一个,两个,三个的分别有多少。好像从小到大转移要方便一些。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cctype>
#define qmin(x,y) (x=min(x,y))
#define qmax(x,y) (x=max(x,y))
using namespace std;

inline char gc() {
//  static char buf[100000],*p1,*p2;
//  return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
    return getchar();
}

template<class T>
int read(T &ans) {
    ans=0;char ch=gc();T f=1;
    while(!isdigit(ch)) {
        if(ch==EOF) return -1;
        if(ch=='-') f=-1;
        ch=gc();
    }
    while(isdigit(ch))
        ans=ans*10+ch-'0',ch=gc();
    ans*=f;return 1;
}

template<class T1,class T2>
int read(T1 &a,T2 &b) {
    return read(a)!=EOF&&read(b)!=EOF?2:EOF;
}

template<class T1,class T2,class T3>
int read(T1 &a,T2 &b,T3 &c) {
    return read(a,b)!=EOF&&read(c)!=EOF?3:EOF;
}

typedef long long ll;
const int Maxn=310;
const int inf=0x3f3f3f3f;
const int mod=1000000007;

int n,x,a[4];
double f[Maxn][Maxn][Maxn];

signed main() {
//  freopen("test.in","r",stdin);
    read(n);
    for(int i=1;i<=n;i++) {
        read(x);
        a[x]++;
    }
    for(int i=0;i<=n;i++)
        for(int j=0;j<=n-i;j++)
            for(int k=0;k<=n-i-j;k++) {
                if(!i&&!j&&!k) continue;
                double x=i+j+k,p=(double)n/x;
                if(i) f[i][j][k]+=f[i-1][j+1][k]*i/x;
                if(j) f[i][j][k]+=f[i][j-1][k+1]*j/x;
                if(k) f[i][j][k]+=f[i][j][k-1]*k/x;
                f[i][j][k]+=p;
            }
    printf("%.10lf",f[a[3]][a[2]][a[1]]);
    return 0;
}

K 记忆化搜索,如果你知道博弈论,那就很简单了。

L 这个总觉得在哪里见过,不过结论也很简单,就直接记左右端点DP,长度由小到大枚举即可。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cctype>
#define qmin(x,y) (x=min(x,y))
#define qmax(x,y) (x=max(x,y))
using namespace std;

inline char gc() {
//  static char buf[100000],*p1,*p2;
//  return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
    return getchar();
}

template<class T>
int read(T &ans) {
    ans=0;char ch=gc();T f=1;
    while(!isdigit(ch)) {
        if(ch==EOF) return -1;
        if(ch=='-') f=-1;
        ch=gc();
    }
    while(isdigit(ch))
        ans=ans*10+ch-'0',ch=gc();
    ans*=f;return 1;
}

template<class T1,class T2>
int read(T1 &a,T2 &b) {
    return read(a)!=EOF&&read(b)!=EOF?2:EOF;
}

template<class T1,class T2,class T3>
int read(T1 &a,T2 &b,T3 &c) {
    return read(a,b)!=EOF&&read(c)!=EOF?3:EOF;
}

typedef long long ll;
const int Maxn=3100;
const int inf=0x3f3f3f3f;
const int mod=1000000007;

int n;
ll f[Maxn][Maxn],a[Maxn];

signed main() {
//  freopen("test.in","r",stdin);
    read(n);
    for(int i=1;i<=n;i++) read(a[i]);
    for(int i=1;i<=n;i++)
        for(int j=i;j<=n;j++) {//[j-i+1,j]
            int l=j-i+1,r=j,len=n-i;
            f[l][r]=max(a[l]-f[l+1][r],a[r]-f[l][r-1]);
        }
    printf("%lld",f[1][n]);
    return 0;
}

M 首先\(O(nk^2)\)的DP很简单,然后用前缀和优化一下就好了。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cctype>
#define qmin(x,y) (x=min(x,y))
#define qmax(x,y) (x=max(x,y))
using namespace std;

inline char gc() {
//  static char buf[100000],*p1,*p2;
//  return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
    return getchar();
}

template<class T>
int read(T &ans) {
    ans=0;char ch=gc();T f=1;
    while(!isdigit(ch)) {
        if(ch==EOF) return -1;
        if(ch=='-') f=-1;
        ch=gc();
    }
    while(isdigit(ch))
        ans=ans*10+ch-'0',ch=gc();
    ans*=f;return 1;
}

template<class T1,class T2>
int read(T1 &a,T2 &b) {
    return read(a)!=EOF&&read(b)!=EOF?2:EOF;
}

template<class T1,class T2,class T3>
int read(T1 &a,T2 &b,T3 &c) {
    return read(a,b)!=EOF&&read(c)!=EOF?3:EOF;
}

typedef long long ll;
const int Maxn=110000;
const int inf=0x3f3f3f3f;
const int mod=1000000007;

int n,k,x;
ll f[Maxn],g[Maxn];

signed main() {
//  freopen("test.in","r",stdin);
    read(n,k);
    f[0]=1;
    for(int i=1;i<=n;i++) {
        read(x);
        int sum=0;
        for(int j=0;j<=x;j++) {
            sum+=f[j];
            sum%=mod;
            g[j]=sum;
        }
        for(int l=0,r=x+1;r<=k;l++,r++) {
            sum+=f[r]-f[l];
            sum%=mod;
            sum+=mod;
            sum%=mod;
            g[r]=sum;
        }
        memcpy(f,g,sizeof(f));
    }
    printf("%d",f[k]);
    return 0;
}

N 合并石子不解释。

O 枚举前i个men,匹配了哪i个women的方案数,注意i是没必要记的。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cctype>
#define qmin(x,y) (x=min(x,y))
#define qmax(x,y) (x=max(x,y))
using namespace std;

inline char gc() {
//  static char buf[100000],*p1,*p2;
//  return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
    return getchar();
}

template<class T>
int read(T &ans) {
    ans=0;char ch=gc();T f=1;
    while(!isdigit(ch)) {
        if(ch==EOF) return -1;
        if(ch=='-') f=-1;
        ch=gc();
    }
    while(isdigit(ch))
        ans=ans*10+ch-'0',ch=gc();
    ans*=f;return 1;
}

template<class T1,class T2>
int read(T1 &a,T2 &b) {
    return read(a)!=EOF&&read(b)!=EOF?2:EOF;
}

template<class T1,class T2,class T3>
int read(T1 &a,T2 &b,T3 &c) {
    return read(a,b)!=EOF&&read(c)!=EOF?3:EOF;
}

typedef long long ll;
const int Maxn=3100000;
const int inf=0x3f3f3f3f;
const ll mod=1000000007;

int n,a[30][30],f[Maxn];

signed main() {
//  freopen("test.in","r",stdin);
    read(n);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            read(a[i][j]);
    int end=1<<n;f[0]=1;
    for(int i=1;i<end;i++) {
        int cnt=0;
        for(int j=1,nh=1;j<=n;j++,nh<<=1)
            if(i&nh) cnt++;
        for(int j=1,nh=1;j<=n;j++,nh<<=1)
            if((i&nh)&&a[cnt][j]) f[i]=(f[i]+f[i^nh])%mod;
    }
    printf("%d\n",f[end-1]);
    return 0;
}

P 很简单的入门树形DP。

Q 首先\(O(n^2)\)的DP很简单,然后用数据结构优化即可。

R 首先\(O(kn^2)\)的DP很简单,然后用矩乘优化即可。

S 简单的数位DP,具体还是看代码吧。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define qmax(x,y) (x=max(x,y));
using namespace std;

typedef long long ll;
const int Maxn=11000;
const ll mod=1000000007;

char a[Maxn];
int k,f[Maxn][110],ans;

int main() {
    scanf("%s",a);
    scanf("%d",&k);
    int n=strlen(a);
    f[0][0]=1;
    for(int i=1;i<n;i++)
        for(int l=0;l<=9;l++)
        for(int j=0;j<=k;j++) f[i][(j+l)%k]=(f[i][(j+l)%k]+f[i-1][j])%mod;
    int sum=0;
    for(int i=0;i<n;sum=(sum+a[i]-'0')%k,i++)
        for(int j=0;j<a[i]-'0';j++) {
            ans+=f[n-i-1][(k-(sum+j)%k)%k];
            ans%=mod;
        }
    if(sum==0) ans++;ans--;ans+=mod;
    ans%=mod;
    printf("%d",ans);
    return 0;
}

T 记fij为前i个数其中第i个数排名为j的方案数,然后用前缀和和后缀和维护即可。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cctype>
#define qmin(x,y) (x=min(x,y))
#define qmax(x,y) (x=max(x,y))
using namespace std;

inline char gc() {
//  static char buf[100000],*p1,*p2;
//  return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
    return getchar();
}

template<class T>
int read(T &ans) {
    ans=0;char ch=gc();T f=1;
    while(!isdigit(ch)) {
        if(ch==EOF) return -1;
        if(ch=='-') f=-1;
        ch=gc();
    }
    while(isdigit(ch))
        ans=ans*10+ch-'0',ch=gc();
    ans*=f;return 1;
}

template<class T1,class T2>
int read(T1 &a,T2 &b) {
    return read(a)!=EOF&&read(b)!=EOF?2:EOF;
}

template<class T1,class T2,class T3>
int read(T1 &a,T2 &b,T3 &c) {
    return read(a,b)!=EOF&&read(c)!=EOF?3:EOF;
}

typedef long long ll;
const int Maxn=31000;
const int inf=0x3f3f3f3f;
const ll mod=1000000007;

int f[Maxn],pre[Maxn],suf[Maxn],n;

char readch() {
    char ch=gc();
    while(ch!='>'&&ch!='<') ch=gc();
    return ch;
}

signed main() {
//  freopen("test.in","r",stdin);
    read(n);f[1]=pre[1]=suf[1]=1;
    for(int i=2;i<=n;i++) {
        if(readch()=='<') for(int j=1;j<=i;j++) f[j]=pre[j-1];
        else for(int j=1;j<=i;j++) f[j]=suf[j];
        for(int j=1;j<=i;j++) pre[j]=(pre[j-1]+f[j])%mod;
        for(int j=i;j>=1;j--) suf[j]=(suf[j+1]+f[j])%mod;
    }
    printf("%d",pre[n]);
    return 0;
}

U 常见的枚举子集的套路,转移的系数要预处理一下,然后就是\(O(3^n)\)

#include<cstdio>
#include<cstring>
#include<algorithm>
#define qmax(x,y) (x=max(x,y));
using namespace std;

typedef long long ll;
const int Maxn=110000;
const ll mod=1000000007;

int n,a[21][21];
ll f[Maxn],g[Maxn];

int main() {
    scanf("%d",&n);int end=1<<n;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++) scanf("%d",&a[i][j]);
    for(int i=0;i<end;i++)
        for(int j=1,tempp=1;j<=n;j++,tempp<<=1)
            if(i&tempp)
                for(int k=j,tempt=tempp;k<=n;k++,tempt<<=1)
                    if(i&tempt) f[i]+=a[j][k];
    for(int i=0;i<end;i++) g[i]=f[i];
    for(int i=0;i<end;i++)
        for(int s=i&(i-1);s;s=i&(s-1)) qmax(g[i],g[i^s]+f[s]);
    printf("%lld",g[end-1]); 
    return 0;
}

V 换根DP,具体请参考代码。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define qmax(x,y) (x=max(x,y));
using namespace std;

typedef long long ll;
const int Maxn=210000;
const ll mod=1000000007;

int to[Maxn],nxt[Maxn],first[Maxn],tot=1;
ll f[Maxn],g[Maxn],pr[Maxn],su[Maxn],p[Maxn],ans[Maxn],m;
int n,u,v;

inline void add(int u,int v) {
    to[tot]=v;
    nxt[tot]=first[u];
    first[u]=tot++;
    to[tot]=u;
    nxt[tot]=first[v];
    first[v]=tot++;
}

void dfs(int root,int fa) {
    int tot=0;
    f[root]++;
    for(int i=first[root];i;i=nxt[i])
        if(to[i]!=fa) {
            dfs(to[i],root);
            f[root]=f[root]*f[to[i]]%m;
        }
    for(int i=first[root];i;i=nxt[i]) if(to[i]!=fa) p[++tot]=to[i];
    f[root]++;f[root]%=m;
    pr[0]=su[tot+1]=1;
    for(int i=1;i<=tot;i++) pr[i]=pr[i-1]*f[p[i]]%m;
    for(int i=tot;i>=1;i--) su[i]=su[i+1]*f[p[i]]%m;
    for(int i=1;i<=tot;i++) g[p[i]]=pr[i-1]*su[i+1]%m;
}

void dfs2(int root,int fa) {
    if(root==1) ans[root]=1;
    else ans[root]=(g[root]*ans[fa]+1)%m;
    for(int i=first[root];i;i=nxt[i])
        if(to[i]!=fa) dfs2(to[i],root);
}

int main() {
    scanf("%d%lld",&n,&m);
    for(int i=1;i<n;i++) {
        scanf("%d%d",&u,&v);
        add(u,v);
    }
    dfs(1,1);
    dfs2(1,1);
    for(int i=1;i<=n;i++) printf("%lld\n",ans[i]*(m+f[i]-1)%m);
    return 0;
}

W 首先把所有的区间按照右端点排序,然后从1到n依次考虑,设fi为在第i个位置为1的最大值,那么我们每次令这个值为前面所有值的最大值,然后把扫过的区间加在这些值上,这个就可以用线段树做了。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cctype>
#define qmin(x,y) (x=min(x,y))
#define qmax(x,y) (x=max(x,y))
using namespace std;

inline char gc() {
//  static char buf[100000],*p1,*p2;
//  return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
    return getchar();
}

template<class T>
int read(T &ans) {
    ans=0;char ch=gc();T f=1;
    while(!isdigit(ch)) {
        if(ch==EOF) return -1;
        if(ch=='-') f=-1;
        ch=gc();
    }
    while(isdigit(ch))
        ans=ans*10+ch-'0',ch=gc();
    ans*=f;return 1;
}

template<class T1,class T2>
int read(T1 &a,T2 &b) {
    return read(a)!=EOF&&read(b)!=EOF?2:EOF;
}

template<class T1,class T2,class T3>
int read(T1 &a,T2 &b,T3 &c) {
    return read(a,b)!=EOF&&read(c)!=EOF?3:EOF;
}

typedef long long ll;
const int Maxn=3100000;
const int inf=0x3f3f3f3f;
const ll mod=1000000007;

int tl[Maxn],tr[Maxn],n,m;
ll tn[Maxn],flag[Maxn];

void build(int root,int l,int r) {
    tl[root]=l;tr[root]=r;
    int mid=l+r>>1;
    if(l==r) return;
    build(root<<1,l,mid);
    build((root<<1)|1,mid+1,r);
}

inline void update(int root) {
    tn[root]=max(tn[root<<1],tn[(root<<1)|1]);
}

void pushdown(int root) {
    if(flag[root]) {
        flag[root<<1]+=flag[root];
        tn[root<<1]+=flag[root];
        flag[(root<<1)|1]+=flag[root];
        tn[(root<<1)|1]+=flag[root];
        flag[root]=0;
    }
}

ll query(int root,int x) {
    int l=tl[root],r=tr[root],mid=l+r>>1;
    if(r==x) return tn[root];
    pushdown(root);
    if(x<=mid) return query(root<<1,x);
    else return max(tn[root<<1],query((root<<1)|1,x));
}

void change(int root,int l,int r,ll x) {
    int lc=tl[root],rc=tr[root],mid=lc+rc>>1;
    if(l<=lc&&r>=rc) {
        flag[root]+=x;
        tn[root]+=x;
        return;
    }
    pushdown(root);
    if(l<=mid) change(root<<1,l,r,x);
    if(r>mid) change((root<<1)|1,l,r,x);
    update(root);
}

struct node {
    int l,r;
    ll x;
}b[Maxn];

int cmp(node a,node b) {
    return a.r<b.r;
}

signed main() {
//  freopen("test.in","r",stdin);
    read(n,m);
    for(int i=1;i<=m;i++)
        read(b[i].l,b[i].r,b[i].x);
    sort(b+1,b+m+1,cmp);
    build(1,1,n);
    int zhy=1,nh=1;
    for(int i=1;i<=n;i++) {
        change(1,i,i,query(1,i));
        while(b[nh].r==i) {
            change(1,b[nh].l,b[nh].r,b[nh].x);
            nh++;
        }
    }
    printf("%lld",max(tn[1],0ll));
    return 0;
}

X 神仙の结论:按照w+s排序然后背包即可。

至于为什么是对的,可以感性理解一下。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cctype>
#define qmin(x,y) (x=min(x,y))
#define qmax(x,y) (x=max(x,y))
using namespace std;

inline char gc() {
//  static char buf[100000],*p1,*p2;
//  return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
    return getchar();
}

template<class T>
int read(T &ans) {
    ans=0;char ch=gc();T f=1;
    while(!isdigit(ch)) {
        if(ch==EOF) return -1;
        if(ch=='-') f=-1;
        ch=gc();
    }
    while(isdigit(ch))
        ans=ans*10+ch-'0',ch=gc();
    ans*=f;return 1;
}

template<class T1,class T2>
int read(T1 &a,T2 &b) {
    return read(a)!=EOF&&read(b)!=EOF?2:EOF;
}

template<class T1,class T2,class T3>
int read(T1 &a,T2 &b,T3 &c) {
    return read(a,b)!=EOF&&read(c)!=EOF?3:EOF;
}

typedef long long ll;
const int Maxn=31000;
const int inf=0x3f3f3f3f;
const ll mod=1000000007;

struct node {
    int w,s;
    ll v;
}a[Maxn];

int n;
ll f[Maxn],ans;

int cmp(node a,node b) {
    return a.w+a.s<b.w+b.s;
}

signed main() {
//  freopen("test.in","r",stdin);
    read(n);
    for(int i=1;i<=n;i++)
        read(a[i].w,a[i].s,a[i].v);
    sort(a+1,a+n+1,cmp);
    for(int i=1;i<=n;i++)
        for(int j=a[i].s;j>=0;j--)
            qmax(f[j+a[i].w],f[j]+a[i].v);
    for(int i=0;i<Maxn;i++) qmax(ans,f[i]);
    printf("%lld\n",ans);
    return 0;
}

Y 这个跟前面的类似,可以用容斥算答案,但是暴力容斥是\(O(n^3)\)的,很显然通过不了本题。

但是我们可以注意到把这些点排序后,这个转移的时候是在一个DAG上转移的,那么我们在这个DAG上直接转移,时间复杂度即可降为\(O(n^2)\)。从点i到点j的方案数为\(C_{|x_i-x_j|+|y_i-y_j|}^{|x_i-x_j|}\)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cctype>
#define qmin(x,y) (x=min(x,y))
#define qmax(x,y) (x=max(x,y))
using namespace std;

inline char gc() {
//  static char buf[100000],*p1,*p2;
//  return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
    return getchar();
}

template<class T>
int read(T &ans) {
    ans=0;char ch=gc();T f=1;
    while(!isdigit(ch)) {
        if(ch==EOF) return -1;
        if(ch=='-') f=-1;
        ch=gc();
    }
    while(isdigit(ch))
        ans=ans*10+ch-'0',ch=gc();
    ans*=f;return 1;
}

template<class T1,class T2>
int read(T1 &a,T2 &b) {
    return read(a)!=EOF&&read(b)!=EOF?2:EOF;
}

template<class T1,class T2,class T3>
int read(T1 &a,T2 &b,T3 &c) {
    return read(a,b)!=EOF&&read(c)!=EOF?3:EOF;
}

typedef long long ll;
const int Maxn=210000;
const int inf=0x3f3f3f3f;
const ll mod=1000000007;

ll jc[Maxn],ijc[Maxn],inv[Maxn],n,m,q,f[Maxn];

struct node {
    ll x,y;
}a[Maxn];

ll C(int n,int k) {
    return jc[n]*ijc[k]%mod*ijc[n-k]%mod;
}

int cmp(node a,node b) {
    return a.x==b.x?a.y<b.y:a.x<b.x;
}

signed main() {
//  freopen("test.in","r",stdin);
    read(n,m);
    inv[0]=inv[1]=1;
    for(int i=2;i<=n+m;i++) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
    jc[0]=ijc[0]=1;
    for(int i=1;i<=n+m;i++) jc[i]=jc[i-1]*i%mod,ijc[i]=ijc[i-1]*inv[i]%mod;
    read(q);
    for(int i=1;i<=q;i++) read(a[i].x,a[i].y);
    sort(a+1,a+q+1,cmp);
    a[0]=(node){1,1};a[++q]=(node){n,m};
    f[0]=1;
    for(int i=0;i<q;i++) for(int j=i+1;j<=q;j++) if(a[i].y<=a[j].y)
        f[j]=(mod+f[j]-f[i]*C(a[j].x-a[i].x+a[j].y-a[i].y,a[j].x-a[i].x)%mod)%mod;
    printf("%lld",(mod-f[q])%mod);
    return 0;
}

Z 蒟蒻表示不会斜率优化,于是学了一晚上。

所以这道题就很裸了啊,直接斜率优化就好了,如果不会的话可以百度一下。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cctype>
#define qmin(x,y) (x=min(x,y))
#define qmax(x,y) (x=max(x,y))
using namespace std;

inline char gc() {
//  static char buf[100000],*p1,*p2;
//  return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
    return getchar();
}

template<class T>
int read(T &ans) {
    ans=0;char ch=gc();T f=1;
    while(!isdigit(ch)) {
        if(ch==EOF) return -1;
        if(ch=='-') f=-1;
        ch=gc();
    }
    while(isdigit(ch))
        ans=ans*10+ch-'0',ch=gc();
    ans*=f;return 1;
}

template<class T1,class T2>
int read(T1 &a,T2 &b) {
    return read(a)!=EOF&&read(b)!=EOF?2:EOF;
}

template<class T1,class T2,class T3>
int read(T1 &a,T2 &b,T3 &c) {
    return read(a,b)!=EOF&&read(c)!=EOF?3:EOF;
}

typedef long long ll;
const int Maxn=310000;
const int inf=0x3f3f3f3f;
const ll mod=1000000007;

int n;
ll C,h[Maxn],f[Maxn];

struct node {
    ll x,y;
}a[Maxn];

signed main() {
//  freopen("test.in","r",stdin);
    read(n,C);
    for(int i=1;i<=n;i++) read(h[i]);
    int zhy=1,nh=0;
    a[++nh]=(node){-2*h[1],h[1]*h[1]};
    for(int i=2;i<=n;i++) {
        while(zhy!=nh&&h[i]*a[zhy+1].x+a[zhy+1].y<h[i]*a[zhy].x+a[zhy].y) zhy++;
        f[i]=a[zhy].x*h[i]+a[zhy].y+C+h[i]*h[i];
        ll x=-2*h[i],y=f[i]+h[i]*h[i];
        while(zhy!=nh&&1.0*(y-a[nh].y)*(a[nh].x-a[nh-1].x)>1.0*(a[nh].y-a[nh-1].y)*(x-a[nh].x)) nh--;
        a[++nh]=(node){-2*h[i],f[i]+h[i]*h[i]};
    }
    printf("%lld",f[n]);
    return 0;
}

转载于:https://www.cnblogs.com/shanxieng/p/10232228.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值