【省選模擬】20/06/05

AA

  • ldxldx 大神的 6060 分做法:
    用二元組 (a,b)(a,b) 記錄最小段數和最小代價來 dpdp,記 dpidp_i 表示以 ii 結尾的最小值
    考慮如何爲一個區間選擇一個最優的匹配點,暴力枚舉 dpjdp_j,考慮將某一個區間從 jj 挪到 ii,這對應一個關於 i,ji,j 的二元一次函數,用三元組 (a,b,c)(a,b,c) 可以表示出這個係數
    係數預處理是個二維平面修改,差分後求矩陣的前綴和,可以做到 O(n2)O(n^2)

  • 這個 dpdp 看起來不能優化了,考慮一些性質,設貪心出來選出來的點爲 p1,p2,,pkp_1,p_2,\dots,p_k,那麼 (p0,p1],(p1,p2],,(pk1,pk](p_0,p_1],(p_1,p_2],\dots,(p_{k-1},p_k] 的每個區間必然存在且僅存在一個點,顯然大於一個點是不可能的,若存在一個區間沒有點,那麼必定存在一個區間滿足 li(pt1,pt],ri=ptl_i\in (p_{t-1},p_t],r_i=p_t,不合法

  • 同時注意到,一個區間的中點必定可以定位到兩個點之間,且最優值只會取到兩個端點的一個
    前綴和簡單詢問每個區間的係數 coefl,rcoef_{l,r},於是可以暴力 dpdp
    dpi=mindpj+coefj,i(j(pt1,pt])dp_{i}=\min dp_j+coef_{j,i}(j\in(p_{t-1},p_t]),又注意到對於 coefi,r,coefj,r(i<j)coef_{i,r},coef_{j,r}(i<j)ii 的增量始終大於 jj 的增量,可以分治解決

#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;
}

BB

  • 點分樹即可,用 zkwzkw 線段樹維護一下子樹的某個區間的最近距離,O(nlog2n)O(n\log^2 n),可以跑過 O(nlogn)O(n\log n) 的線段樹分治 + 虛樹
#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;
}

CC

  • 枚舉點積角度可以死去
  • 考慮一個等價的過程,枚舉一條邊以及角度,積有貢獻的點集的面積

在這裏插入圖片描述

  • 變成積一個前綴的面積,發現每次要處理的就是 SBCDdC\int S_{BCD}\text{d}C

在這裏插入圖片描述

  • BCsinB+C=ABsinC\frac{BC}{\sin B+C}=\frac{AB}{\sin C}, S=ABBCsinB=tsinCsinB+CS=AB*BC*\sin B=t*\frac{\sin C}{\sin B+C} 其中 tt 爲常量,考慮積分:
    0RsinCsinB+CdC=cosBRsinBBR+BcosCsinCdC\int_0^{R}\frac{\sin C}{\sin B+C}\text{d}C\\ =\cos B *R- \sin B\int_B^{R+B}\frac{\cos C}{\sin C}\text{d}C
    cosxsinxdx=1sinxdsinx=lnsinx\int \frac{\cos x}{\sin x}\text{d}x=\int \frac{1}{\sin x}\text{d}\sin x=\ln \sin x
#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;

} 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章