Atcoder Educational DP Contest

时间:2022-08-31 18:43:51

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

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

Atcoder Educational DP Contest的更多相关文章

  1. Atcoder Educational DP Contest 题解

    A - Frog 1/B - Frog 2 入门... #include<cstdio> #define abs(a) ((a)>=0?(a):(-(a))) #define min ...

  2. Atcoder Educational DP Contest I - Coins &lpar;概率DP&rpar;

    题意:有\(n\)枚硬币,每枚硬币抛完后向上的概率为\(p[i]\),现在求抛完后向上的硬币个数大于向下的概率. 题解:我们用二维的\(dp[i][j]\)来表示状态,\(i\)表示当前抛的是第\(i ...

  3. Sth about Educational DP Contest

    Contest Website : atcoder.jp/contests/dp \[\begin{array}{c|C|c|c} TaskNum & TaskName & Statu ...

  4. 【DP】Educational DP Contest

    这份 dp 题单的最后几题好难 orz. 前面的题比较简单,所以我会选取一些题来讲,其它的直接看代码理解吧 qwq. 传送门: https://atcoder.jp/contests/dp 全部 AC ...

  5. Educational DP Contest H - Grid 1 &lpar;DP&rpar;

    题意:有一个\(n\)X\(m\)的图,"#"表示障碍物,"."表示道路,只能向右或向下走,问从左上角走到右下角的方案数. 题解:这题可以用bfs来搞,但dp更 ...

  6. Educational DP Contest G - Longest Path &lpar;dp&comma;拓扑排序&rpar;

    题意:给你一张DAG,求图中的最长路径. 题解:用拓扑排序一个点一个点的拿掉,然后dp记录步数即可. 代码: int n,m; int a,b; vector<int> v[N]; int ...

  7. Educational DP Contest F - LCS &lpar;LCS输出路径&rpar;

    题意:有两个字符串,求他们的最长公共子序列并输出. 题解:首先跑个LCS记录一下dp数组,然后根据dp数组来反着还原路径,只有当两个位置的字符相同时才输出. 代码: char s[N],t[N]; i ...

  8. Educational DP Contest E - Knapsack 2 &lpar;01背包进阶版&rpar;

    题意:有\(n\)个物品,第\(i\)个物品价值\(v_{i}\),体积为\(w_{i}\),你有容量为\(W\)的背包,求能放物品的最大价值. 题解:经典01背包,但是物品的最大体积给到了\(10^ ...

  9. Atcoder Typical DP Contest S - マス目(状压 dp&plus;剪枝)

    洛谷题面传送门 介绍一个不太主流的.非常暴力的做法( 首先注意到 \(n\) 非常小,\(m\) 比较大,因此显然以列为阶段,对行的状态进行状压.因此我们可以非常自然地想到一个非常 trivial 的 ...

随机推荐

  1. CKEDITOR最新版不能上传图片的解决

    文献:http://bbs.csdn.net/topics/390883077 代码例子:http://download.csdn.net/download/itmyhome/7851265 1.原先 ...

  2. 。【自学总结 1】------3ds Max 界面

    3ds Max 界面包含4部分(7区域) 4部分:菜单.控制工具.命令面板.窗口区 7区域: 1.标题栏:主要用于显示当前工作文件的名称,可以看到文件存储路径. 2.菜单栏:菜单中的命令如果带有省略号 ...

  3. php验证复选框有效性的示例

    本文介绍一个简单的php通过代码验证复选框值的有效性,有需要的可以参考一下 验证复选框的php代码,如下: 复制代码代码如下: <?php   /**   * 在php中验证复选框的有效性  * ...

  4. stack 集合栈计算机 (摘)

    有一个专门为了集合运算而设计的“集合栈”计算机.该机器有一个初始为空的栈,并且支持以下操作:PUSH:空集“{}”入栈DUP:把当前栈顶元素复制一份后再入栈UNION:出栈两个集合,然后把两者的并集入 ...

  5. PHP实现微信随机红包算法和微信红包的架构设计简介

    微信红包的架构设计简介: 原文:https://www.zybuluo.com/yulin718/note/93148 @来源于QCon某高可用架构群整理,整理朱玉华. 背景:有某个朋友在朋友圈咨询微 ...

  6. IDEA之HttpServletRequest之报错解决方案

    @Controller public class UserController { @RequestMapping("/selectUser") public String sel ...

  7. Effective Java Chapter4 Classes and Interface

    MInimize the accessibility of classes and members 这个叫做所谓的 information hiding ,这么做在于让程序耦合度更低,增加程序的健壮性 ...

  8. 编程使用缓冲流读取试题文件,test6&lowbar;5&period;txt 内容如下所示。 每次显示试题文件中的一道题目,读取到字符&OpenCurlyDoubleQuote;&ast;”时暂停读取, 等待用户从键盘输入答案。用户做完全部题目后,程序给出用户的得分。

    test6_5.txt内容如下: (1)面向对象程序设计中,把对象的属性和行为组织在同一个模块内的机制叫做( ). A.封装象 B.继承 C.抽象 D.多态 ******************** ...

  9. c语言实现xor加密

    异或运算:^ 定义:它的定义是:两个值相同时,返回false,否则返回true.也就是说,XOR可以用来判断两个值是否不同. 特点:如果对一个值连续做两次 XOR,会返回这个值本身. ^ // 第一次 ...

  10. 第一个socket服务端程序

    第一个socket服务端程序 #include <stdio.h> #include <stdlib.h> #include <string.h> #include ...