2018.8.20 附加赛3
A 斯(贪心)
某状态下的最差情况收益就是min(wa,wb)-cost。当然是从大的取,同时尽量保证wa,wb均衡。
枚举一下,Ans时刻取个max就行了。
复杂度在于排序 O(nlogn)。Ai只有四位小数且<=10,可以用桶排降到O(n)。
//138ms 2392kb
#include <cstdio>
#include <cctype>
#include <algorithm>
//#define gc() getchar()
#define MAXIN 300000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
const int N=1e5+5;
int n;
double A[N],B[N];
char IN[MAXIN],*SS=IN,*TT=IN;
inline double read()
{
double x=0,y=0.1; register char c=gc();
for(; !isdigit(c)&&c!='.'; c=gc());
for(; isdigit(c); x=x*10+c-'0',c=gc());
for(c=='.'&&(c=gc()); isdigit(c); x+=(c-'0')*y,y*=0.1,c=gc());
return x;
}
int main()
{
n=read();
for(int i=1; i<=n; ++i) A[i]=read();
for(int i=1; i<=n; ++i) B[i]=read();
std::sort(A+1,A+1+n), std::sort(B+1,B+1+n);
double Ans=0,nowa=0,nowb=0;
for(int a=n,b=n,cost=0; a||b; ++cost,Ans=std::max(Ans,std::min(nowa,nowb)-cost))
{
if(!b||(nowa<=nowb && a)) nowa+=A[a--];
else nowb+=B[b--];
}
printf("%.4lf
",Ans);
return 0;
}
B 给(DP)
DP,f[i][j]表示当前有i个叶子,树中根到所有叶子的路径中向左的边都不超过j条,的方案数。
根节点的两棵左右子树算是两个独立的子问题,即枚举左子树的叶节点数k后,可以直接f[k]*f[i-k]拼起来。
再具体就是左子树中所有向左边不超过j-1条的叶节点都可以接上i,然后乘上右子树所有路径向左边不超过j条的方案数。
即 (f[i][j]=sum_{k=1}^{j-1}f[k][j-1]*f[i-k][j])。
至于为什么只枚举左儿子而不用*2。。不知道。。求dalao解答。。(枚举是对称的?)
这样是O(n^3),得分35~40。
注意到转移是个卷积形式,用NTT转移 写的好的话能拿到60。
满分做法:还是要换种状态表示,考虑枚举一个一个放叶子:f[i][j]表示已有i个叶子,当前放的叶子到根节点的路径有j条向左边。
下一个点要么放在当前叶子的左儿子。这样不会改变叶子数,j要加1,即转移到f[i][j+1]。
要么放在最近有左儿子但是没放右儿子的点,作为其右儿子。这样能转移到叶子数+1,j-1的状态,即f[i+1][j-1]。
f[i][0]就表示已有i个叶子,且所有有左儿子的点都有右儿子了,即k=i时的答案。
因为是枚举了所有状态下的唯二两种转移。。嗯。。所以说(这么说)没问题吧。。
复杂度O(n^2)。
唉 感觉两个DP都好迷啊。。
//376ms 95800kb(莫名慢好多 明明都差不多)
#include <cstdio>
#define mod (998244353)
#define rg register
//#define Add(x,v) x+=v,x>=mod&&(x-=mod)
const int N=5005;
int f[N][N];
inline void Add(int &x,int v){
x+=v, x>=mod&&(x-=mod);
}
int main()
{
int m,n; scanf("%d%d",&m,&n);
f[1][0]=1;
for(rg int i=1; i<n; ++i)
{
Add(f[i][1],f[i][0]);
for(rg int j=1; j<m; ++j)
Add(f[i][j+1],f[i][j]), Add(f[i+1][j-1],f[i][j]);
}
for(rg int i=1; i<=n; ++i) printf("%d
",f[i][0]);
return 0;
}
C 普