【翔哥的胡策】互测3.10(斜率优化dp+计算几何||manacher)

本文通过两个问题探讨了斜率优化DP和计算几何的应用。T1中详细解释了如何利用斜率优化DP解决CEOI2004锯木厂选址问题,涉及到距离、权值和成本的计算。T2则介绍了POI2007的OSI-Axes of Symmetry问题,讨论了计算几何和Manacher算法在处理轴对称和环形序列回文串问题上的应用。
摘要由CSDN通过智能技术生成

T1:

[CEOI2004]锯木厂选址

题解:

首先几个数组all[i]表示前i的权值和
dis[i]表示从1到i的距离
c[i]表示从1运到i树的花费,那么 c[i]=c[i1]+all[i1]s[i1] c [ i ] = c [ i − 1 ] + a l l [ i − 1 ] ∗ s [ i − 1 ]

w[i][j]表示从i树运到j树,j为厂的花费,那么

w[i][j]=c[j]c[i1]all[i1](dis[j]dis[i1]) w [ i ] [ j ] = c [ j ] − c [ i − 1 ] − a l l [ i − 1 ] ∗ ( d i s [ j ] − d i s [ i − 1 ] )

那么设f[i]为在i处建立第二个厂的最小花费,那么

f[i]=minc[j]+w[j+1][i]+w[i+1][n+1]j<i f [ i ] = m i n c [ j ] + w [ j + 1 ] [ i ] + w [ i + 1 ] [ n + 1 ] , j < i

把w带进去,最后f[i]为

f[i]=dis[i]all[j]+all[j]dis[j]+c[n+1]all[i](dis[n+1]dis[i]) f [ i ] = − d i s [ i ] ∗ a l l [ j ] + a l l [ j ] ∗ d i s [ j ] + c [ n + 1 ] − a l l [ i ] ∗ ( d i s [ n + 1 ] − d i s [ i ] )

诶?斜率优化dp?K=-all[j],B=all[j]*dis[j]?

特别要注意,斜率优化dp的时候head++是T除head不优的值,tail–是T除被覆盖的直线,注意这一步必不可少。
顺带复习一下单调队列是head++T除旧的,tail–是T除不优的

代码:

#include<cstdio>
#include<iostream>
#define LL long long
#define INF 1e9
using namespace std;
int n,q[20005];LL all[20005],s[20005],dis[20005],c;
LL K(int j) {return -all[j];}
LL B(int j) {return all[j]*dis[j];}
LL Y(int i,int j) {return K(j)*dis[i]+B(j);}
bool cover(int x1,int x2,int x3)//x2被盖掉 
{
    LL w1=(K(x1)-K(x3))*(B(x2)-B(x1));
    LL w2=(K(x1)-K(x2))*(B(x3)-B(x1));
    return w1<=w2;
}
int main()
{
    freopen("collect.in","r",stdin);
    freopen("collect.out","w",stdout);
    scanf("%d",&n);
    for (int i=1;i<=n;i++) scanf("%lld%lld",&all[i],&s[i]),all[i]+=all[i-1];
    for (int i=2;i<=n+1;i++) c+=all[i-1]*(LL)s[i-1],dis[i]=dis[i-1]+s[i-1];

    int head=1,tail=0;LL minn=INF;
    for (int i=1;i<=n;i++)
    {
        while (head<tail && Y(i,q[head])>=Y(i,q[head+1])) head++;
        minn=min(minn,Y(i,q[head])+c-all[i]*(dis[n+1]-dis[i]));
        while (head<tail && cover(i,q[tail],q[tail-1])) tail--;
        q[++tail]=i; 
    }
    printf("%lld",minn);
}

T2:

[POI2007]OSI-Axes of Symmetry

题解:

法一:计算几何

我们分为n为奇数/偶数两种情况讨论
这里写图片描述

这里写图片描述

然后经过喵喵喵痛苦努力的尝试用向量解决问题,终于放弃了。。。。

问了问wtt才知道可以用文化课的方法ax+by+c=0解决问题
那么中垂线就是bx-ay+c=0
两条直线垂直就是a1*a2+b1*b2=0
然后最后一个点T了,我常数这么大
然后发现如果用dcmp判断与0的关系的话会慢,所以直接用fabs和eps判断会快
这样可能会顺利一(hen)些(duo)

代码:

#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
const double eps=1e-8;
struct po
{
    double x,y;
    po(double X=0,double Y=0){x=X;y=Y;}
}p[100005];
struct line{double a,b,c;};
int n,ans;
double dis(po a,line L)
{
    double t=sqrt(L.a*L.a+L.b*L.b);
    if (fabs(t)==0) return -1;
    return fabs((L.a*a.x+L.b*a.y+L.c)/t);
}
line get_line(po a,po b)//过a,b的直线 
{
    line L;
    L.a=b.y-a.y;
    L.b=a.x-b.x;
    L.c=a.y*b.x-a.x*b.y;
    return L;
}
bool DC(po a,po b,line L)
{
    line L1=get_line(a,b);
    return (fabs(L.a*L1.a+L.b*L1.b)<eps)&&(fabs(dis(a,L)-dis(b,L))<eps);
}
bool On(po a,line L){return fabs(L.a*a.x+L.b*a.y+L.c)<eps;}
void Point()
{
    int nn=n;
    n/=2;
    for (int i=1;i<=n;i++)
    {
        po a=p[i],b=p[i+n];
        line L=get_line(a,b);
        int vv=1;
        for (int j=1;j<n;j++)
        {
            int b1=(i+j)%nn;if (!b1) b1=nn;
            int b2=(i-j+nn)%nn;if (!b2) b2=nn;
            po c=p[b1],d=p[b2];
            if (!DC(c,d,L)) {vv=0;break;}
        }
        ans+=vv;
    }
}
void Line()
{
    for (int i=1;i<=n;i++)
    {
        int b1=(i+1)%n;if (!b1) b1=n;
        po a=p[i],b=p[b1],zd=po((a.x+b.x)/2,(a.y+b.y)/2);
        line L=get_line(a,b);
        swap(L.a,L.b); L.b=-L.b;
        L.c=-L.a*zd.x-L.b*zd.y;
        int vv=1;
        for (int j=0;j<=n/2;j++)
        {
            int b1=(i-j+n)%n;if (!b1) b1=n;
            int b2=(i+j+1)%n;if (!b2) b2=n;
            po c=p[b1],d=p[b2];
            if (b1==b2)
            {
                if (!On(c,L)) {vv=0;break;}
            }
            else
            {
                if (!DC(c,d,L)) {vv=0;break;}
            } 
        }
        ans+=vv;
    }
    if (n%2==0) ans/=2;
} 
int main()
{
    freopen("homework.in","r",stdin);
    freopen("homework.out","w",stdout);
    int T;scanf("%d",&T);
    while (T--)
    {
        scanf("%d",&n);
        for (int i=1;i<=n;i++) scanf("%lf%lf",&p[i].x,&p[i].y);
        ans=0;
        Line();
        if (n%2==0) Point();
        printf("%d\n",ans);
    }
}

法二:hash+manacher

表面是计算几何,实际上没多大关系。
把每条边和每个角转换为数值,求环形序列的回文串的个数。
转换方法:边→长度的平方,角→向量积的模。
环形序列回文串:加倍线形序列
回文串问题字符串:Manacher

代码:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
struct po
{
    int x,y;
    po(int X=0,int Y=0){x=X;y=Y;}
}p[150005];
int n,ans,len,w[800005],a[800005];
po operator -(po a,po b){return po(a.x-b.x,a.y-b.y);}
int cj(po a,po b,po c)
{
    po x=b-a,y=b-c;
    return x.y*y.x-x.x*y.y;
}
int lenth(po a,po b){return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y);}
void manacher()
{
    int maxx=0,pos=0,mx=0;
    for (int i=0;i<n;i++)
    {
        if (mx>i) w[i]=min(w[pos*2-i],mx-i);
        else p[i]=1;
        while (i-w[i]>=0 && i+w[i]<n && a[i-w[i]]==a[i+w[i]]) w[i]++;
        if (w[i]+i>mx) mx=w[i]+i,pos=i;
        if (w[i]*2-1>len) ans++; 
    }
    ans/=2;
}
int main()
{
    int T;scanf("%d",&T);
    while (T--)
    {
        memset(w,0,sizeof(w));
        scanf("%d",&n);
        for (int i=1;i<=n;i++) scanf("%d%d",&p[i].x,&p[i].y);
        ans=0;int num=0;
        for (int i=1;i<=n;i++)
        {
            a[num++]=cj(p[(i-2+n)%n+1],p[i],p[i%n+1]);
            a[num++]=lenth(p[i],p[i%n+1]);
        }
        len=num;
        for (int i=0;i<len;i++) a[num++]=a[i];
        n=num; manacher();
        printf("%d\n",ans);
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值