zoukankan      html  css  js  c++  java
  • 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
    ",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
    ",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
    ",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
    ",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;
    }
    
    
    
  • 相关阅读:
    汇编中push寄存器的影响
    windows lsp 指导
    java匹配中文汉字的正则表达式
    php中preg_match用户名正则实例
    Javascript中while和do-while循环用法详解
    如何找出MySQL数据库中的低效SQL语句
    asp.net连接oracle的问题及方法总结
    asp.net运算符之逻辑运算符以及其他运算符
    用命令行将Java程序打包为jar文件
    php 中cookie和session的用法比较
  • 原文地址:https://www.cnblogs.com/shanxieng/p/10232228.html
Copyright © 2011-2022 走看看