您的位置:首页 > 其它

APIO2007-2015题解大集合(2010年篇)

2016-04-16 13:45 218 查看
好消息:这一篇没有上古警告!

特别行动队

经典斜率优化DP。
关于斜率优化dp的式子有两种推法:第一种是按照直线Y=kx+b的形式,把与i有关的看成b,与i,j都相关的看成kx,其中与i相关的是k,与j相关的是x,然后画个图来推;第二种是考虑两个决策点j,k,对它们的决策做差,然后把i有关的移到一边,会形成(f[j]-f[k])/(j-k)>p*i(与i相关的一坨)的形式。其中第一种能够应用的情况比较广泛,因为第二种推法到最后可能会出现移项时不等式符号不好判断的问题。
这个题以第二种推法为例,第一种推法留给读者思考。
设f[i]为所求最大战斗力,则
f[i]=f[j]+a*(s[i]-s[j])^2+b(s[i]-s[j])+c
于是对于j优于k,有
f[j]+a*s[i]^2+a*s[j]^2-2as[i]s[j]+b*s[i]-b*s[j]+c>f[k]+a*s[i]^2+a*s[k]^2-2as[i]s[k]+b*s[i]-b*s[k]+c
f[j]-f[k]>a*(s[k]^2-s[j]^2)+2as[i](s[j]-s[k])+b(s[j]-s[k])
(f[j]+a*s[j]^2-b*s[j])-(f[k]+a*s[k]^2-b*s[j])>2as[i]*(s[j]-s[k])
然后把s[j]-s[k]除过去,就是所求的式子。照着这个式子写斜率优化即可。另外注意斜率用double很慢,有些毒瘤出题人可能会卡几个点必须用分子分母分开除法转乘法才能过……
#include<iostream>
#include<iomanip>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#define LL long long
using namespace std;
LL n,a,b,c,head,tail,q[1000005],f[1000005],BEGIN[1000005],s[1000005];
double Slope(LL j,LL k)
{return double(f[k]-f[j]+a*(s[k]*s[k]-s[j]*s[j])-b*(s[k]-s[j]))/double(2*a*(s[k]-s[j]));
}
void Solve()
{f[0]=0,head=1,tail=1;//2as单增
for(LL i=1;i<=n;i++)
{while(head<tail&&Slope(q[head],q[head+1])<=double(s[i]))head++;
f[i]=f[q[head]]+a*(s[i]-s[q[head]])*(s[i]-s[q[head]])+b*(s[i]-s[q[head]])+c;
while(head<tail&&Slope(q[tail-1],q[tail])>Slope(q[tail],i))tail--;
q[++tail]=i;
}
}
void Input()
{scanf("%lld%lld%lld%lld",&n,&a,&b,&c);
s[0]=0;
for(LL i=1;i<=n;i++)
{scanf("%lld",&BEGIN[i]);
s[i]=s[i-1]+BEGIN[i];
}
}
int main()
{Input();
Solve();
printf("%lld\n",f
);
return 0;
}
/*
4
-1 10 -20
2 2 3 4
*/


巡逻

此题其实是一道NOIP难度的题目……这个题有三种做法:

1、树上的插头DP。这个题目可以转化为求树上不相交的最长链,于是可以用插头乱搞……然而我并不会做……

2、考虑k=1的情况,则只需要找树的最长链即可。考虑k=2,则现在图是一个环套树,仍然需要在图上找出最能节省路程的一条边。这个时候把树的最长链提出来,由于成环的那条路只能走一次,另一次是不能走那条边的,所以还是可以当成一条链来处理。于是有两种情况:
(1)在最长链上某个节点的子树内部进行拼接:直接对最长链上每个点子树进行最长链DP然后取最长+次长即可。

(2)中间要经过一段最长链,易知此时路程应该为节省路程-中间走过的路程长度。写出状态转移方程不难发现这是一个可以用单调栈解决的简单DP,具体方程留给读者自己思考。
3、先求一次最长链,把最长链上长度设为-1,再求一次最长链即可。(代码最后良心地给了几组数据,要的拿走就好……)
#include<iostream>
#include<iomanip>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<queue>
using namespace std;
int cnt=0,ans,wer,n,k,a,b,h[200005]={0},vis[200005],dist[200005],from[200005],stack[200005],D[200005],Maxx[200005],Sec[200005];
int qu[500005];
struct node{int next,to;}edge[400005];
queue<int>q;
void Addedge(int x,int y)
{cnt++,edge[cnt].to=y,edge[cnt].next=h[x],h[x]=cnt;
}
void Input()
{scanf("%d%d",&n,&k);
for(int i=1;i<n;i++)
{scanf("%d%d",&a,&b);
Addedge(a,b),Addedge(b,a);
}
}
void Dfs(int x)
{vis[x]=1;
for(int i=h[x];i;i=edge[i].next)
{int y=edge[i].to;
if(!vis[y])dist[y]=dist[x]+1,from[y]=x,Dfs(y);
}
}
void Dfs2(int x)
{vis[x]=1,Maxx[x]=0,Sec[x]=0;
for(int i=h[x];i;i=edge[i].next)
{int y=edge[i].to;
if(vis[y])continue;
dist[y]=dist[x]+1,Dfs2(y);
if(Maxx[y]+1>Maxx[x])Sec[x]=Maxx[x],Maxx[x]=Maxx[y]+1;
else if(Maxx[y]+1>Sec[x])Sec[x]=Maxx[y]+1;
}
wer=min(wer,ans-Maxx[x]-Sec[x]+1);
}
void Dp()//链上的DP
{int head=1,tail=1;qu[1]=0;
for(int i=1;i<=stack[0];i++)
{wer=min(wer,ans-(qu[head]+Maxx[stack[i]]-D[stack[i]]-1));
while(head<=tail&&qu[tail]<Maxx[stack[i]]+D[stack[i]])tail--;
qu[++tail]=Maxx[stack[i]]+D[stack[i]];
}
}
void Solve()
{int S=1,maxx=0,rec;
memset(vis,0,sizeof(vis)),memset(from,0,sizeof(from));
dist[S]=0,from[S]=0;
Dfs(S);
for(int i=1;i<=n;i++)
if(dist[i]>maxx)maxx=dist[i],rec=i;
S=rec;
memset(vis,0,sizeof(vis)),memset(from,0,sizeof(from));
dist[S]=0,from[S]=0;
Dfs(S);
maxx=0;
for(int i=1;i<=n;i++)
if(dist[i]>maxx)maxx=dist[i],rec=i;
ans=2*(n-1)-maxx+1;
wer=999999999;
if(k==1){printf("%d\n",ans);return;}
memset(vis,0,sizeof(vis));
while(!q.empty())q.pop();
int now=rec;
while(now)stack[++stack[0]]=now,vis[now]=1,dist[now]=0,now=from[now];
for(int i=1,j=stack[0];i<j;i++,j--)swap(stack[i],stack[j]);
for(int i=1;i<=stack[0];i++)D[stack[i]]=i-1;
for(int i=1;i<=stack[0];i++)Dfs2(stack[i]);
Dp();
printf("%d\n",wer);
}
int main()
{//freopen("patrol.in","r",stdin);
//freopen("patrol.out","w",stdout);
Input();
Solve();
fclose(stdin);
fclose(stdout);
return 0;
}
/*
13 1
1 2
3 1
3 4
4 9
9 10
10 11
10 12
12 13
5 3
7 5
8 5
5 6
*/
/*
8 2
1 2
3 1
3 4
5 3
7 5
8 5
5 6
*/
/*
7 1
1 2
2 3
3 4
4 5
5 6
6 7
*/
/*
21 2
1 2
2 3
3 4
1 5
5 6
6 7
1 8
8 9
9 10
8 11
8 12
9 13
9 14
1 15
15 18
18 21
15 16
15 17
18 19
18 20
*/
/*
19 2
1 2
2 3
3 4
4 5
5 6
6 7
7 8
8 9
9 10
10 11
11 12
12 13
6 14
14 15
15 16
8 17
17 18
18 19
*/


信号覆盖

我觉得这题黄学长说得比我好,于是就在黄学长基础上改了一下……

这个题如果考虑三角形反而不好做,应该考虑四边形。画图易知,考虑一个四边形任取3个点取4次,在每次一定落入的3个点的基础上,凸四边形会有2次4个点都在内,凹四边形会有1次(简记为凸四边形贡献为2,凹多边形贡献为1)。设凹四边形个数为a,凸四边形个数为b,那么b=C(n,4)-a。于是枚举凹四边形的中间点,以中间点为原点,把其他点按极角排序,枚举极角差刚刚不小于π的两条边,那么这两条边之间的点和其中一条边上的点不包含中间点。枚举的时间复杂度是O(n)。设p为不包含中间点的三角形个数,那么以该点为中间点的凹四边形个数为C(n-1,3)-p。最后的期望即为(a+2*b)/C(n,3)。

#include<iostream>
#include<iomanip>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#define LL long long
using namespace std;
LL n,ans1,ans2,top;
struct node{LL x,y;double ang;}point[1505],temp[1505];
void Input()
{scanf("%lld",&n);
if(n==3){printf("3.000000\n");exit(0);}
for(LL i=1;i<=n;i++)
{scanf("%lld%lld",&point[i].x,&point[i].y);
}
}
bool Comp(node x,node y)
{return x.ang<y.ang;
}
LL Cross(node p1,node p2,node p0)
{return (p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y);
}
LL Inner(LL x)
{LL ret=(n-1)*(n-2)*(n-3)/6;
top=0;
for(LL i=1;i<=n;i++)
if(x==i)temp[0]=point[i];
else temp[++top]=point[i];
for(LL i=1;i<=top;i++)
temp[i].ang=atan2(temp[i].y-temp[0].y,temp[i].x-temp[0].x);
sort(temp+1,temp+top+1,Comp);
LL R=2,num=0;
for(LL i=1;i<=top;i++)
{while(Cross(temp[i],temp[R],temp[0])>=0)
{R=R%top+1,num++;
if(R==i)break;
}
ret-=num*(num-1)/2,num--;
}
return ret;
}
void Solve()
{ans1=0;
for(LL i=1;i<=n;i++)
ans1+=Inner(i);
ans2=n*(n-1)*(n-2)*(n-3)/24-ans1;
double wer=(double(2*ans2+ans1))/double(n*(n-1)*(n-2)/6.0);
printf("%.6lf\n",wer+3);
}
int main()
{Input();
Solve();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: