题解
首先声明:由于今天一道题都没有做出来…第一题只骗了30分(真的是题目太难…orz)所以以下的题目标程都是大佬提供的…
第一题——国王的难题(king)
【题目描述】
- 有n+1个点,其中n个点在x轴上,另一个点单独拎出来,从第k个点出发,求这些点的哈密顿回路。
这道题真的是扎心啊..大清早打了两个小时的分类讨论…(真的以为是数学题..)但是很不幸的是我打错了orz,所想到的并不是最优解… 然后就爆零了…,但其实这道题真的蛮难的。是codeforce上的30D..
正解是这样子的啊..还是分类讨论。
- 先要进行整体的处理啊..将前面的几个点排一遍,获得序列 a1..an a 1 . . a n 。
首先有一种情况,就是起点是在那一个单独的点p。那就是可以证明回路长度是:
dist(a1,an)+min(dist(a1,p),dist(an,p)) d i s t ( a 1 , a n ) + m i n ( d i s t ( a 1 , p ) , d i s t ( a n , p ) )
- 第二种就是起点不在那个单独的p上,那么可以证明x轴上存在点k使得人从1~k走再回来,再往k+1~n,或者k+1~n走再回来,再往1~k。
- 然后就要考虑,从p点出发,走进一个区间 [l,r] [ l , r ] ,最佳方案就是从p出发走到 l l ,当中较近的一个,然后再走到p,距离就是 dist(al,ar)+min(dist(al,p).dist(ar,p)) d i s t ( a l , a r ) + m i n ( d i s t ( a l , p ) . d i s t ( a r , p ) )
- 从k点出发,走一个区间 [l,r] [ l , r ] 在来到p,距离就是 abs(al−ar)+min(abs(ak−al)+dist(r),abs(ak−al)+dist(l)) a b s ( a l − a r ) + m i n ( a b s ( a k − a l ) + d i s t ( r ) , a b s ( a k − a l ) + d i s t ( l ) )
- 然后就是枚举k计算出答案。复杂度是 O(n) O ( n ) 。
0
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 100100
using namespace std;
int n,k;
double newtag;
double xx[N];
double ans;
double y;
double get_dis(int l)
{
return sqrt((xx[l]-xx[n+1])*(xx[l]-xx[n+1])+y*y);
}
double dis[N];
double to[N];
double ca1(int l,int r)
{
return xx[r]-xx[l]+min(get_dis(l),get_dis(r));
}
double ca2(int l,int r)
{
return xx[r]-xx[l]+min(get_dis(l)+abs(newtag-xx[r]),get_dis(r)+abs(newtag-xx[l]));
}
int main()
{
// freopen("king.in", "r", stdin);
// freopen("king.out", "w", stdout);
scanf("%d%d",&n,&k);
for(int i=1;i<=n+1;i++)
{
int x;
scanf("%lf",&xx[i]);
}
scanf("%lf",&y);
newtag=xx[k];
sort(xx+1,xx+n+1);
if(k==n+1)
{
double ans=ca1(1,n);
printf("%.10lf\n",ans);
}else
{
double ans=ca2(1,n);
for(int i=1;i<n;i++)
{
ans=min(ans,min(ca1(1,i)+ca2(i+1,n),ca2(1,i)+ca1(i+1,n)));
}
printf("%.10lf\n",ans);
}
return 0;
}
第二题——别怕,DravDe很善良(dravde)
【题目描述】
- 给定一个四元组(v,c,l,r),要求出一个子序列使得满足:
- 子序列当中的c+l+r相等。
- 第一个元素l=0,最后一个元素r=0。
- 第i个元素的l等于前所有个元素的c之和
- 整体子序列的v之和尽量的大。
- 真的第一题打完之后我就没心情来打这道题了..而且还是这么恶心的dp。查了下,是CF上的28D。怕教练是在CF上乱扒题的orz。
- 看大佬的神仙代码我真的没有想到这么短..但确实很难理解orz。讲下算法。
- 由于对于当前的车辆需要前面和后面的人数是固定的,所以需要筛出这些点了。
- 利用map求出对于固定的l之后的r+c的点,那么当前这给点就是c所需要的,对应的之前的点的附加值。然后在更新map当中l所对应的r+c的值(求最大orz)。
- 这个代码真的是神仙。
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <cstdio>
#include <map>
using namespace std;
void fff(){
freopen("dravde.in","r",stdin);
freopen("dravde.out","w",stdout);
}
const int MAXN=100001;
int n;
map<int,int> m[MAXN];
int f[MAXN],s[MAXN],p[MAXN],cnt=0,z;
int main(){
// fff();
scanf("%d",&n);
for (int i=1;i<=n;i++){
int v,c,l,r;
scanf("%d%d%d%d",&v,&c,&l,&r);
if(r+c<MAXN&&(!l||m[l].find(r+c)!=m[l].end())){
int u=m[l][r+c];
f[i]=f[u]+v,p[i]=u;
if(!r&&f[i]>f[z]){
z=i;
}
if(f[i]>f[m[l+c][r]]){
m[l+c][r]=i;
}
}
}
while (z){
s[cnt++]=z;
z=p[z];
}
printf("%d\n",cnt);
while (cnt--){
printf("%d ",s[cnt]);
}
return 0;
}
第三题——复杂易记的密码(tricky)
【题目描述】
- 给定字符串S,求一个奇数位的回文串T使得:
- S1,S2,S3为任意串,可以为空串。求满足条件的字符串长度最大为多少。
- 这也是一道CF的题orz。但应该是所有题目当中最简单的一道了。30E
- 其实思想是比较好想的,在考试的时候已经想的差不多了但时间不够orz。
- 可以把字符串翻过来,那么回文串的头就有了,然后与翻转前的进行比较,最大程度进行匹配。剩下的中间部分就是可以用马拉车进行最大的回文串半径求得。
/**
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int n,i,j,k,f[100011],g[100011],dl[100011],m[100011],s,e,mid,ans;
unsigned hl[100011],hr[100011],pow[100011];
char c[100011];
unsigned hash(int s,int e)
{
if (s<e) return hl[e]-hl[s]*pow[e-s];
else return hr[e]-hr[s]*pow[s-e];
}
int calc(int x)
{
int s=x-f[x]+1,e=x+f[x]-1;
return f[x]*2-1+min(g[m[s-1]],n-e)*2;
}
int main()
{
freopen("tricky.in", "r", stdin);
freopen("tricky.out", "w", stdout);
scanf("%s",c+1);
n=strlen(c+1);
for (i=1;i<=n;i++) hl[i]=hl[i-1]*29+c[i];
for (i=n;i>=1;i--) hr[i]=hr[i+1]*29+c[i];
pow[0]=1;
for (i=1;i<=n;i++) pow[i]=pow[i-1]*29;
for (i=1;i<=n;i++)
{
s=1;
e=min(i,n-i+1);
while (s!=e)
{
mid=s+e-(s+e)/2;
if (hash(i,i+mid-1)==hash(i,i-mid+1)) s=mid;
else e=mid-1;
}
f[i]=s;
}
s=1;
e=0;
for (i=1;i<=n;i++)
{
if (c[i]==c[n]) dl[++e]=i;
while (s<=e && hash(dl[s],i)!=hash(n,n-(i-dl[s])) && n-(i-dl[s])>i) s++;
if (s>e) g[i]=0;
else g[i]=i-dl[s]+1;
}
for (i=1;i<=n;i++)
{
m[i]=m[i-1];
if (g[m[i]]<g[i]) m[i]=i;
}
for (i=1;i<=n;i++)
ans=max(ans,calc(i));
for (i=1;i<=n;i++)
if (ans==calc(i))
{
s=i-f[i]+1;
e=i+f[i]-1;
j=min(g[m[s-1]],n-e);
k=m[s-1]-g[m[s-1]]+1;
if (j) cout << 3 << endl
<< k << " " << j << endl
<< s << " " <<e-s+1 << endl
<< n-j+1 << " " << j << endl;
else cout << 1 << endl << s << " " << e-s+1 << endl;
return 0;
}
return 0;
}
- 程序当中更加的暴力 orz,直接求出串所对应的hash来求出中间的回文串orz。
- 首先求下以每个字符为中心的最长回文串的长度f[i]
- 然后发现suffix的后面没有加东西,那么我们可以对每个位置求以这个位置结尾最长能多长跟最后匹配,记为g[i]
- 然后再定义m[i],满足g[m[i]]=max(g[1]..g[i])
- 然后对于i作为middle的中心,他能形成的最长原串长度为f[i]*2-1+2*min(g[m[i-f[i]]],n-(i+f[i]-1))