T1:
题解:
首先几个数组all[i]表示前i的权值和
dis[i]表示从1到i的距离
c[i]表示从1运到i树的花费,那么
c[i]=c[i−1]+all[i−1]∗s[i−1]
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[i−1]−all[i−1]∗(dis[j]−dis[i−1]) 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:
题解:
法一:计算几何
我们分为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);
}
}