@suixinhita
2017-10-24T08:52:39.000000Z
字数 3642
阅读 978
...反正就是挂挂挂..也没什么可以说的..
直接题解
emm...基本还是很好想到是要直接dfs换成序列来做的,但是之后求区间有多少大于的就蒙蔽了...
问题可以等价于区间第k大(但是可以很简单)
本来试图苟个主席树,后来没苟住,放弃了。
其实可以换个方向考虑。
考试的时候一直试图排序区间,无果,之后发现是排序权值更好一点。
正确的解法就是dfs转化成区间。
之后对于每一个节点都进行权值的排序,在序列相应位置按照权值大小加入,查询即可。
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=1e6+5;
struct data{
int id,l,r,p;
}a[N];
struct edge{
int to,next;
}e[N];
int head[N],gs;
void adde(int fr,int to)
{
e[++gs].to=to,e[gs].next=head[fr],head[fr]=gs;
}
int tr[N],n,cnt;
int lowbit(int x) {return x&(-x);}
int getsum(int p)
{
int an=0;
while(p)
{
an+=tr[p];
p-=lowbit(p);
}
return an;
}
void add(int p,int x)
{
while(p<=n)
{
tr[p]+=x;
p+=lowbit(p);
}
}
int ans[N];
void dfs(int u)
{
a[u].l=++cnt;
for(int i=head[u];i;i=e[i].next)
{
int v=e[i].to;
dfs(v);
}
a[u].r=cnt;
}
bool cmp(data x,data y) {return x.p>y.p;}
void solve()
{
dfs(1);
sort(a+1,a+1+n,cmp);
for(int i=1;i<=n;++i)
{
ans[a[i].id]=getsum(a[i].r)-getsum(a[i].l-1);
add(a[i].l,1);
}
for(int i=1;i<=n;++i) printf("%d\n",ans[i]);
}
inline int read()
{
int x=0;char c=getchar();
while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
return x;
}
int main()
{
int y;
n=read();
for(int i=1;i<=n;++i) a[i].p=read(),a[i].id=i;
for(int i=2;i<=n;++i) y=read(),adde(y,i);
solve();
return 0;
}
其实这个题我属于一种:???的状态,因为我并不知道考场上这些大佬怎么a掉的...
正确解法可以算作一个分数规划(?)
首先我们可以知道,一个解是最优的当且仅当这个解分配的每一个 即使加上了 个也没什么用处,加到其他任意一层都差不多。
化成式子就是:
#include<cmath>
#include<cstdio>
#include<iostream>
#include<algorithm>
#define ll long long
#define db double
using namespace std;
const db eps=1e-10;
const int N=1e6+5;
int n;
ll K;
db a[N];
bool check(db x)
{
ll ret=0;
for(int i=1;i<=n;++i)
{
ll tmp=ceil(((-1.0+sqrt(1.0+4.0*(a[i]/x)))/2.0));
ret+=tmp;
if(ret>=K) return 0;
}
return 1;
}
void solve()
{
db l=0,r=1e10;
while(fabs(l-r)>eps)
{
db mid=(l+r)/2.0;
if(check(mid)) r=mid;
else l=mid;
}
ll sum=0;
db val=0;
for(int i=1;i<=n;++i)
{
ll c=ceil(((-1.0+sqrt(1.0+4.0*(a[i]/l)))/2.0));
sum+=c;
val+=a[i]/c;
}
val-=((db)K-(db)sum)*l;
cout<<(ll)(val+0.5)<<endl;
// printf("%lld",(ll)val+0.4);
}
int main()
{
freopen("1.txt","r",stdin);
scanf("%d%lld",&n,&K);
for(int i=1;i<=n;++i) scanf("%lf",&a[i]);
solve();
return 0;
}
说实在的我也还没苟住为什么这个是个区间dp...神佬们可能什么都能苟吧...
所以说这道题就是区间dp,状态更多一些。
设置状态
于是记忆化搜索之,可以了。
注意:记忆化的时候,如果 或者 要返回
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int inf=0x3f3f3f3f;
int dp[51][51][51][51];
int n,a[51];
int solve(int l,int r,int i,int j)
{
if(l>r) return 0;
if(i>j) return 0;
if(dp[l][r][i][j]!=-1) return dp[l][r][i][j];
dp[l][r][i][j]=-inf;
if(l+1<=50&&r-1>=1) dp[l][r][i][j]=max(solve(l,r-1,i,j),solve(l+1,r,i,j));
if(i+1<=n) dp[l][r][i][j]=max(dp[l][r][i][j],solve(l,r,i+1,j)+(a[i]==l));
if(j-1>=1) dp[l][r][i][j]=max(dp[l][r][i][j],solve(l,r,i,j-1)+(a[j]==r));
if(i+1<=n&&j-1>=1) dp[l][r][i][j]=max(dp[l][r][i][j],solve(l,r,i+1,j-1)+(a[i]==r)+(a[j]==l));
// if(dp[l][r][i][j]>0) printf("%d %d %d %d %d\n",l,r,i,j,dp[l][r][i][j]);
return dp[l][r][i][j];
}
int main()
{
freopen("1.txt","r",stdin);
freopen("11.txt","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;++i) scanf("%d",&a[i]);
memset(dp,-1,sizeof(dp));
for(int i=1;i<=n;++i)
for(int k=1;k<=a[i];++k)
for(int f=a[i];f<=50;++f)
dp[k][f][i][i]=1;
int ans=solve(1,50,1,n);
/* for(int i=1;i<=n;++i)
for(int j=i;j<=n;++j)
for(int l=1;l<=50;++l)
for(int r=1;r<=50;++r)
{
if(dp[l][r][i][j]<0) continue;
printf("%d %d %d %d %d\n",l,r,i,j,dp[l][r][i][j]);//<<endl;
}
*/ printf("%d",ans);
return 0;
}