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;
}
Atcoder Educational DP Contest的更多相关文章
- Atcoder Educational DP Contest 题解
A - Frog 1/B - Frog 2 入门... #include<cstdio> #define abs(a) ((a)>=0?(a):(-(a))) #define min ...
- Atcoder Educational DP Contest I - Coins (概率DP)
题意:有\(n\)枚硬币,每枚硬币抛完后向上的概率为\(p[i]\),现在求抛完后向上的硬币个数大于向下的概率. 题解:我们用二维的\(dp[i][j]\)来表示状态,\(i\)表示当前抛的是第\(i ...
- AtCoder Educational DP Contest 总结
前言 感觉都初一升初二了,再做这个题是不是有点太菜了啊-- 里面大概都是些 DP 板子题(确信,题目质量还挺高的,不过不涉及太难的优化(实际上只有最后一题是斜率优化). 不管了,还是写个 blog 来 ...
- Sth about Educational DP Contest
Contest Website : atcoder.jp/contests/dp \[\begin{array}{c|C|c|c} TaskNum & TaskName & Statu ...
- 【DP】Educational DP Contest
这份 dp 题单的最后几题好难 orz. 前面的题比较简单,所以我会选取一些题来讲,其它的直接看代码理解吧 qwq. 传送门: https://atcoder.jp/contests/dp 全部 AC ...
- Educational DP Contest H - Grid 1 (DP)
题意:有一个\(n\)X\(m\)的图,"#"表示障碍物,"."表示道路,只能向右或向下走,问从左上角走到右下角的方案数. 题解:这题可以用bfs来搞,但dp更 ...
- Educational DP Contest G - Longest Path (dp,拓扑排序)
题意:给你一张DAG,求图中的最长路径. 题解:用拓扑排序一个点一个点的拿掉,然后dp记录步数即可. 代码: int n,m; int a,b; vector<int> v[N]; int ...
- Educational DP Contest F - LCS (LCS输出路径)
题意:有两个字符串,求他们的最长公共子序列并输出. 题解:首先跑个LCS记录一下dp数组,然后根据dp数组来反着还原路径,只有当两个位置的字符相同时才输出. 代码: char s[N],t[N]; i ...
- Educational DP Contest E - Knapsack 2 (01背包进阶版)
题意:有\(n\)个物品,第\(i\)个物品价值\(v_{i}\),体积为\(w_{i}\),你有容量为\(W\)的背包,求能放物品的最大价值. 题解:经典01背包,但是物品的最大体积给到了\(10^ ...
随机推荐
- android 网络监测
public class NetWorkStateReceiver extends BroadcastReceiver { @Override public void onReceive(Contex ...
- 非受限联合体 - 现代C++新特性总结
非受限联合体 非受限联合体:C++98中并不是所有数据类型都能够成为union的数据成员,不允许联合体拥有非POD(Plain Old Data).静态或引用类型的成员. C++11中取消了联合体对于 ...
- python 根据路径导入模块
Import python module NOT on path http://stackoverflow.com/questions/10161568/import-python-module-no ...
- (转)es进行聚合操作时提示Fielddata is disabled on text fields by default
根据es官网的文档执行 GET /megacorp/employee/_search { "aggs": { "all_interests": { " ...
- 使用gunicorn部署Flask项目
[*] 本文出处:http://b1u3buf4.xyz/ [*] 本文作者:B1u3Buf4 [*] 本文授权:禁止转载 从自己的博客移动过来. gunicorn是一个python Wsgi的WEB ...
- chkconfig添加进入服务后,出现的现象
比如在php-fpm添加服务中,一部分脚步如下 #!/bin/sh # # php-fpm - this script starts and stops the php-fpm ...
- http-equiv制作跳转欢迎页面
做网站嘛,没有个欢迎页面肿么能忍? 要忽悠下浏览者的嘛. 然后此处省略一万字... 目的:不需要其他的脚本,直接在meta中控制时间,跳转页面 原理很简单: < meta http-equiv= ...
- 【转】Redis之发布 订阅模式
本例包括 jedis_demo:入口类 jedis_control:jedis控制器(jedis的连接池) jedis_pub_sub_listener:订阅的监听器 singleton_agent: ...
- mysql存储引擎管理使用
mysql采用插件化架构,可以支持不同的存储引擎,比如myisam,innodb.本文简单的介绍mysql存储引擎的管理与使用. 1.查看mysql存储引擎:show engines; 可以看到,my ...
- sql server数据库备份单个表的结构和数据生成脚本
1.使用场景:sql server数据库备份单个表的结构和数据,在我们要修改正式系统的数据的一天或者多条某些数据时候,要执行update语句操作,安全稳健考虑,最好先做好所修改的表的结构和数据备份! ...