题解:P10695 [SNCPC2024] 商路
WeLikeStudying · · 题解
尝试提供比较简单的思考方式。
题意
在圆的
选出最多的合法线段使它们除了端点外两两不交。
分析
你手玩了几个数据,发现答案不容易很大,你思考如何让答案大一点,于是你写了一个暴力对拍:代码。
你尝试找一些答案不太小的数据,于是用:
int calc(int S)
{
return (S&(S-1))!=0;
}
当然这样子错误率很高:
手玩小数据即可发现,很多数据的答案最后所有的线段共用端点(尤其是你筛选出答案比较大的情况时),既然这样,求出所有的合法线段,组成一个无向图,找到入度最大的点作为答案输出似乎是一种不错的策略,于是你改成:
int b[N];
bool c[N][N];
int calc(int S)
{
fill(b,b+k,0);
up(i,0,k-1)fill(c[i],c[i]+k,0);
up(i,0,k-1)if(S>>i&1)
{
int mx=0;
up(j,0,k-1)
if(S>>j&1)mx=max(mx,dis(i,j));
up(j,0,k-1)
if((S>>j&1)&&dis(i,j)==mx&&i!=j&&!c[j][i])
++b[i],++b[j],c[i][j]=1;
}
return *max_element(b,b+k);
}
虽然连样例都无法正确通过,但是在
考虑只有三个点的情况,一共有三条边,你发现如果最短边小于次短边,那么只可能用到两条边,故判断最短边是否恰好等于次短边,如果是,就在原本答案上加一。
int n,a[N],b[N];
bool c[N][N];
int calc(int S)
{
n=0;
up(i,0,k-1)
if(S>>i&1)a[++n]=i;
up(i,1,n)fill(c[i],c[i]+n+1,0);
fill(b,b+n+1,0);
up(i,1,n)
{
int mx=0;
up(j,1,n)mx=max(mx,dis(a[i],a[j]));
up(j,1,n)if(dis(a[i],a[j])==mx&&i!=j&&!c[j][i])
++b[i],++b[j],c[i][j]=1;
}
bool ok=n==3;
if(ok)
{
int g[3]={a[2]-a[1],a[3]-a[2],a[1]+k-a[3]};
sort(g,g+3);
ok=g[0]==g[1];
}
return *max_element(b,b+n+1)+ok;
}
该代码没有拍出错,我们将原本的枚举改为双指针(因为所有点到某个点的距离先增大后减小),时间复杂度
#include<bits/stdc++.h>
#define up(a,b,c) for(int a=b;a<=c;++a)
const int N=11e4;
using namespace std;
int k,n,a[N],d[N],b[N];
int dis(int u,int v)
{
u=a[u],v=a[v];
if(u>v)swap(u,v);
return min(v-u,u+k-v);
}
int main()
{
cin>>k>>n;
up(i,1,n)cin>>a[i];
int j=1;
up(i,1,n)
{
while(dis(i,j)<=dis(i,j%n+1))
{
if(dis(i,j)==dis(i,j%n+1)&&dis(i,j)!=d[j])++b[i],++b[j];
j=j%n+1;
}
d[i]=dis(i,j);
if(d[i]!=d[j])++b[i],++b[j];
}
bool ok=n==3;
if(ok)
{
int g[3]={a[2]-a[1],a[3]-a[2],a[1]+k-a[3]};
sort(g,g+3);
ok=g[0]==g[1];
}
cout<<*max_element(b,b+n+1)+ok;
return 0;
}
这份代码可以通过本题。