-
大神的 分做法:
用二元組 記錄最小段數和最小代價來 ,記 表示以 結尾的最小值
考慮如何爲一個區間選擇一個最優的匹配點,暴力枚舉 ,考慮將某一個區間從 挪到 ,這對應一個關於 的二元一次函數,用三元組 可以表示出這個係數
係數預處理是個二維平面修改,差分後求矩陣的前綴和,可以做到 -
這個 看起來不能優化了,考慮一些性質,設貪心出來選出來的點爲 ,那麼 的每個區間必然存在且僅存在一個點,顯然大於一個點是不可能的,若存在一個區間沒有點,那麼必定存在一個區間滿足 ,不合法
-
同時注意到,一個區間的中點必定可以定位到兩個點之間,且最優值只會取到兩個端點的一個
前綴和簡單詢問每個區間的係數 ,於是可以暴力 :
,又注意到對於 , 的增量始終大於 的增量,可以分治解決
#include<bits/stdc++.h>
#define cs const
#define pb push_back
#define fi first
#define se second
using namespace std;
namespace IO{
cs int Rlen=1<<22|1;
inline char gc(){
static char buf[Rlen],*p1,*p2;
(p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin));
return p1==p2?EOF:*p1++;
} int read(){
int x=0; char c=gc(); bool f=false;
while(!isdigit(c)) f=c=='-', c=gc();
while(isdigit(c)) x=(((x<<2)+x)<<1)+(c^48), c=gc();
return f?-x:x;
}
} using namespace IO;
typedef long long ll;
cs ll INF = 1e18;
void ckmin(int &a, int b){ if(a > b) a = b; }
void ckmax(int &a, int b){ if(a < b) a = b; }
cs int N = 2e6 + 50;
struct my{ int l, r; };
bool cmp(cs my &a, cs my &b){ return a.l < b.l || (a.l == b.l && a.r > b.r); }
int n, m, Ans; my a[N], b[N];
int rp[N], A; ll Sm[N]; int ct[N];
ll dp[N]; int lm[N], mx[N];
ll calc(int l, int r){
if(l==0) return (ll)r*ct[r]-Sm[r];
if(r>A) return Sm[r]-Sm[l]-(ll)l*(ct[r]-ct[l]);
int mid = (l+r)>>1; assert(l<=r);
ll lv = Sm[mid]-Sm[l]-(ll)l*(ct[mid]-ct[l]);
ll rv = (ll)r*(ct[r]-ct[mid])-(Sm[r]-Sm[mid]);
return lv + rv;
}
void work(int L, int R, int l, int r){
if(L>R) return; int mid = (L+R) >> 1;
int trs = 0; ll mn = INF;
for(int i = max(lm[mid],l); i <= r; i++){
ll t = dp[i] + calc(i,mid);
if(t < mn) mn = t, trs = i;
} dp[mid] = mn;
work(L,mid-1,l,trs); work(mid+1,R,trs,r);
}
int main(){
#ifdef FSYolanda
freopen("1.in","r",stdin);
#endif
scanf("%d",&n);
for(int i=1; i<=n; i++)
a[i].l=read()<<1, a[i].r=read()<<1, A=max(A,a[i].r);
sort(a+1,a+n+1,cmp);
for(int i=1; i<=n; i++){
while(m && a[i].r <= b[m].r) --m;
b[++m] = a[i];
} for(int l=1,r=1;l<=m;l=r){
while(r<=m&&b[r].l<=b[l].r) ++r;
++Ans; rp[Ans] = b[l].r;
} rp[Ans+1] = A+1;
for(int i=1,x; i<=n; i++){
x=(a[i].l+a[i].r)>>1;
++ct[x]; Sm[x]+=x;
}
for(int i=1; i<=A+1; i++)
Sm[i]+=Sm[i-1],ct[i]+=ct[i-1];
for(int i=1; i<=rp[1]; i++) dp[i] = calc(0,i);
for(int i=1; i<=n; i++) mx[a[i].r]=max(mx[a[i].r],a[i].l);
for(int i=1,lp=0; i<=A+1; i++) lm[i]=lp, lp=max(lp,mx[i]);
for(int i=1; i<=Ans; i++)
work(rp[i]+1,rp[i+1],rp[i-1]+1,rp[i]);
cout << Ans << " " << dp[A+1];
return 0;
}
- 點分樹即可,用 線段樹維護一下子樹的某個區間的最近距離,,可以跑過 的線段樹分治 + 虛樹
#include<bits/stdc++.h>
#define cs const
#define pb push_back
#define fi first
#define se second
using namespace std;
namespace IO{
cs int Rlen=1<<22|1;
inline char gc(){
static char buf[Rlen],*p1,*p2;
(p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin));
return p1==p2?EOF:*p1++;
} int read(){
int x=0; char c=gc(); bool f=false;
while(!isdigit(c)) f=c=='-', c=gc();
while(isdigit(c)) x=(((x<<2)+x)<<1)+(c^48), c=gc();
return f?-x:x;
}
} using namespace IO;
cs int N = 1e5 + 50;
cs int INF = 1e9 + 7;
int n, m, fi[N], nxt[N<<1], to[N<<1], w[N<<1], ec;
void adde(int u, int v, int c){
nxt[++ec]=fi[u]; fi[u]=ec; to[ec]=v; w[ec]=c;
}
int d[18][N], fa[N], dep[N];
int sz[N], mx, rt; bool ban[N];
void gsz(int u, int fa){
sz[u]=1;
for(int e=fi[u],v;e;e=nxt[e])
if(!ban[v=to[e]]&&to[e]!=fa)
gsz(v,u),sz[u]+=sz[v];
}
void grt(int u, int fa){
int now=sz[0]-sz[u];
for(int e=fi[u],v;e;e=nxt[e])
if(!ban[v=to[e]]&&to[e]!=fa)
grt(v,u),now=max(now,sz[v]);
if(now<mx) mx=now,rt=u;
}
void dfs(int dt, int u, int fa){
for(int e=fi[u],v;e;e=nxt[e])
if(!ban[v=to[e]]&&to[e]!=fa)
d[dt][v]=d[dt][u]+w[e],dfs(dt,v,u);
}
vector<int> G[N];
void work(int u, int an, int d){
gsz(u,0); sz[0]=mx=sz[u]; rt=0; grt(u,0);
ban[u=rt]=true; fa[u]=an; dep[u]=d;
dfs(d,u,0); if(an) G[an].pb(u);
for(int e=fi[u];e;e=nxt[e])
if(!ban[to[e]]) work(to[e],u,d+1);
}
struct qry{ int d, l, r, c; };
vector<qry> S[N]; int ans[N];
void jmp(int x, int l, int r, int c){
for(int u=x;u;u=fa[u])
S[u].pb((qry){d[dep[u]][x],l,r,c});
}
namespace zkw{
cs int M = 1 << 17;
int mn[M<<1];
void init(){ memset(mn,0x3f,sizeof(mn)); }
void ins(int x, int v){ for(x+=M;x;x>>=1)mn[x]=min(mn[x],v); }
void clr(int x){ for(x+=M;x;x>>=1)mn[x]=INF; }
int qry(int l, int r){
int ans=INF;
for(l+=M-1,r+=M+1;l^r^1;l>>=1,r>>=1){
if(l&1^1) ans=min(ans,mn[l^1]);
if(r&1) ans=min(ans,mn[r^1]);
} return ans;
}
}
void subwork(int u, int dt){
zkw::ins(u,d[dt][u]);
for(int e=0;e<G[u].size();e++)
subwork(G[u][e],dt);
}
void _subwork(int u){
zkw::clr(u);
for(int e=0;e<G[u].size();e++)
_subwork(G[u][e]);
}
void work(int u){
subwork(u,dep[u]);
for(int i=0;i<S[u].size();i++){
qry t=S[u][i];
ans[t.c]=min(ans[t.c],zkw::qry(t.l,t.r)+t.d);
} _subwork(u);
}
int main(){
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
n=read(); zkw::init();
for(int i=1,u,v,w;i<n;i++)
u=read(),v=read(),w=read(),
adde(u,v,w),adde(v,u,w);
work(1,0,0); m=read();
for(int i=1,l,r,x; i<=m; i++){
l=read(), r=read(), x=read();
jmp(x,l,r,i); ans[i]=INF;
} for(int i=1; i<=n; i++)if(S[i].size())work(i);
for(int i=1; i<=m; i++) cout << ans[i] << '\n';
return 0;
}
- 枚舉點積角度可以死去
- 考慮一個等價的過程,枚舉一條邊以及角度,積有貢獻的點集的面積
- 變成積一個前綴的面積,發現每次要處理的就是
- 而 , 其中 爲常量,考慮積分:
#include<bits/stdc++.h>
#define cs const
#define pb push_back
#define fi first
#define se second
using namespace std;
namespace IO{
cs int Rlen=1<<22|1;
inline char gc(){
static char buf[Rlen],*p1,*p2;
(p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin));
return p1==p2?EOF:*p1++;
} int read(){
int x=0; char c=gc(); bool f=false;
while(!isdigit(c)) f=c=='-', c=gc();
while(isdigit(c)) x=(((x<<2)+x)<<1)+(c^48), c=gc();
return f?-x:x;
}
} using namespace IO;
cs int N = 3e3 + 50;
cs double eps = 1e-8, PI = acos(-1.0);
int sgn(double a){ return (a>eps) - (a<-eps); }
struct pnt{
double x, y; pnt(double _x=0, double _y=0){ x=_x; y=_y; }
void input(){ x=read(); y=read(); }
void output(){ cout << x <<" " << y << '\n'; }
pnt operator + (cs pnt &a){ return pnt(x+a.x,y+a.y); }
pnt operator - (cs pnt &a){ return pnt(x-a.x,y-a.y); }
double operator * (cs pnt &a){ return x*a.y-y*a.x; }
pnt operator * (cs double &a){ return pnt(x*a,y*a); }
double Len(){ return x * x + y * y; }
double len(){ return sqrt(x*x+y*y); }
double ang(){ return atan2(y,x); }
};
int n; pnt p[N]; double ans[N];
double ang(pnt a, pnt b, pnt c){
return acos(((b-a).Len()+(c-b).Len()-(c-a).Len())/(2*(c-b).len()*(b-a).len()));
}
int main(){
#ifdef FSYolanda
freopen("1.in","r",stdin);
#endif
n=read();
for(int i=1; i<=n; i++)
p[i].input(),p[i+n]=p[i];
double S=0;
for(int i=1; i<=n; i++)
S+=p[i]*p[i+1];
for(int i=1; i<=n; i++){
double now = 0;
for(int j=i+1; j<i+n-1; j++){
double a = ang(p[j],p[i],p[j+1]);
double b = ang(p[i],p[j],p[j+1]);
ans[i] += a * now + (p[i]-p[j]).Len() * sin(b) *
(a * cos(b) - sin(b) * (log(fabs(sin(a+b))) - log(fabs(sin(b)))));
now += (p[j]-p[i]) * (p[j+1]-p[i]);
}
}
for(int i=1; i<=n; i++){
double Ans = ans[i] - ans[i%n+1];
Ans += (PI - ang(p[i+n-1],p[i],p[i+1])) * S;
printf("%.8lf\n",Ans/S/PI/2);
} return 0;
}