2025ccpc北京市赛
A
题意:
给定一个01串,可以选择一个开头和结尾字符不同的子串,并将其删去,求通过删除操作能产生的字典序最小的字符串为什么?(可以为空)
思路:
如果头尾不同,直接为空
若首尾相同且为1,那么中间如果有子串00则可以删成空。否则不能,此时字符串的形式:101110101101
发现连续的1段肯定大于0的数量,也就是说最后肯定会留下1段。通过人为操作,显然可以留下任何一个1段。所以留最短的
首尾相同且为0同上
C
题意:
给定m个砝码,需要至少表示[1,n],求最大的砝码至少为多少
思路:
二分最大的砝码值X
1,2,4,8,...是最少且最优的砝码选择,可以表示[1,2^m -1]且是最大值最小的选择(如果这样上界还是小于n,则无解)
砝码序列形如:1,2,4,8..,X,X,X
[1,2^m-1]->[1,2\m+X-1]->[1,2\m+2X-1]...
只要上界大于等于n,便可以check到
注意二分右端点范围为1e9左右。
int n,m;
bool check(int mid){
// debug(mid);
int cnt=0;
int ok=0;
for(int i=0;i<m;i++){
if((1ll<<i)>=mid){
ok=1;break;
}
cnt++;
}
if((1ll<<cnt)-1+(m-cnt)*mid>=n){
return true;
}
return false;
}
void solve(){
cin>>n>>m;
if(m<32&&(1ll<<m)-1<n){
cout<<-1<<endl;return;
}
int l=1,r=1e9;
int res=1;
while(l<=r){
int mid=l+r>>1;
if(check(mid)){
r=mid-1;
res=mid;
}else{
l=mid+1;
}
}
cout<<res<<endl;
}
E
题意:
给定一条直线,以及若干个点,在这条直线上找一点P,使得这个点P到每个给定的点距离的最大值最小
思路:
二分点到点的距离d
相当于以题目给定的点为圆心,d为半径
如果d<点到直线距离,显然无解
否则,与直线交与一点或一条弦,P需要在这条弦上
对于所有点,求弦的交集,如果有有解,则check到
具体求圆和直线的交集:
可以把直线当作x轴,垂直于它且经过原点的直线当作y轴
根据点到直线距离公式,算出每个点相对于新的x轴和y轴的坐标
然后很容易求得每条弦的左右两端点
判断交集:
对于所有弦左端点取max->lmax,右端点取min->rmin
lmax<=rmin则有交集
注意浮点数二分,以及浮点数大小之间的比较和精度问题
int n;
int a,b,c;
double caly(int x,int y){
return (1.0*abs(a*x+b*y+c)/sqrtl(a*a+b*b));
}
double calx(int x,int y){
return (1.0*abs(b*x-a*y)/sqrtl(a*a+b*b));
}
bool check(vector<double>&x,vector<double>&y,double mid){
double l=-1e8,r=1e8;
rep(i,1,n){
if(y[i]-mid>=1e-7)return false;
double move=sqrtl(mid*mid-y[i]*y[i]);
double x1=x[i]-move,x2=x[i]+move;
l=max(x1,l);r=min(x2,r);
}
if(r-l>=1e-7)return true;
return false;
}
void work(vector<double>&x,vector<double>&y){
rep(i,1,n){
double dx=calx(x[i],y[i]);
double dy=caly(x[i],y[i]);
if(b*1.0/a*x[i]-y[i]>=1e-7){
x[i]=-dx;
}else x[i]=dx;
y[i]=dy;
}
}
void solve(){
cin>>n;
vector<double>x(n+1);
vector<double>y(n+1);
rep(i,1,n){
cin>>x[i]>>y[i];
}
cin>>a>>b>>c;
double l=0,r=1e8;
double res=0;
work(x,y);
while((r-l)>=1e-8){
double mid=(l+r)/2;
// debug(mid);
if(check(x,y,mid)){
res=mid;
r=mid-1e-7;
}else{
l=mid+1e-7;
}
}
cout<<fixed<<setprecision(15)<<res<<endl;
}
G
题意:
给定一个序列,有两种信息:颜色和亮度,求其子序列长度的最大值
要求子序列:
1.相邻颜色不同
2.相邻亮度不互质
思路:
DP
先不考虑相邻颜色不同的约束
不互质就是有相同质因子
不妨考虑对于质因子p,结尾的数的亮度有p作为因子的子序列长度最大值
将其存储在len1[p]中
考虑当枚举到一个数时:
它的亮度可能由质因子p1,p2构成
如果len[p2]>len[p1]
那么len[p1]可以并需要转移成len[p2](可以理解为牛了p2的子序列)
所以把这个数的所有质因子的len取max,所有质因子的len转移到len_max
所有len[p]可转移成len_max+1
最后遍历因子取max即为答案
考虑相邻颜色需要不同
设计一个数组last,记录下每个质因子len序列最后一个数的颜色
发现对于一个质因子p而言,当遍历到的数(有质因子p)的颜色和last[p]相同时就不能转移了,(无论是通过其他质因子转移成更大的或者是其他质因子借助它转移成更大的)
那么这有可能导致本来len[p]可以通过这个数的其他质因子变成更长的序列(其他质因子也可能用p转移),现在不行了
我们想时刻维护所有的len[p]都是最大的,这样最后肯定是最优的
记len2[p]:为亮度以p的倍数结尾的次长序列且最后一个数的颜色不同于last[p]
那么最长序列最后一个数颜色相同的情况就让len2上,尽可能保证长度最长
在转移时需要注意最长序列和次长序列能否转移
当前dp值大于len1时,那么最长的就是dp,由于是牛了其他质因子的序列,不用考虑p的最后一个数的颜色
次长有可能变为原先的最长,前提是需要保证转移后的最长序列和原先的最长序列最后一个数颜色不同
大于len2小于len1时,可以将len2转移成dp,前提是需要保证当前c[i]不同于last
int dp[maxn];
int ans=1;
int len1[maxn];
int len2[maxn];
int last1[maxn];
int last2[maxn];
void solve(){
int n;cin>>n;
vector<int>w(n+1);
vector<int>c(n+1);
rep(i,1,n)cin>>w[i];
rep(i,1,n)cin>>c[i];
rep(i,1,n){
vector<int>prime;
int temp=w[i];
for(int j=2;j*j<=w[i];j++){
int ok=0;
while(temp%j==0){
temp/=j;
ok=1;
}
if(ok)prime.pb(j);
}
if(temp>1)prime.pb(temp);
int pre=0;
for(int j=0;j<prime.size();j++){
int p=prime[j];
if(c[i]!=last1[p]){
pre=max(pre,len1[p]);
}else{
if(c[i]!=last2[p]){
pre=max(pre,len2[p]);
}
}
}
dp[i]=pre+1;
for(int j=0;j<prime.size();j++){
int p=prime[j];
if(dp[i]>len1[p]){
if(c[i]!=last1[p]){
len2[p]=len1[p];
}
len1[p]=dp[i];
last1[p]=c[i];
}else{
if(dp[i]>len2[p]){
if(c[i]!=last1[p]){
len2[p]=dp[i];
}
}
}
}
ans=max(ans,dp[i]);
}
cout<<ans<<endl;
}

浙公网安备 33010602011771号