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;
}


posted @ 2019-01-07 11:24  shanxizeng  阅读(1406)  评论(1编辑  收藏  举报
广告位招商,有意者请联系