BZOJ4513~4518 SDOI2016 R1 题解

4513: [Sdoi2016]储能表

数位dp,f[i][2][2][2]表示前i位,是否卡n的上界,是否卡m的上界,是否卡k的下界,枚举每一维的下一位直接转移。

#include<cstdio>
#include<cstring>
typedef unsigned u32;
typedef long long ll;
ll x,y,z;
int p,q;
u32 f[61][2][2][2][2];
int main(){
	scanf("%d",&q);
	while(q--){
		scanf("%lld%lld%lld%d",&x,&y,&z,&p);
		memset(f,0,sizeof f);
		f[60][1][1][1][0]=1;
		for(int i=59;~i;--i)
			for(int j=1;~j;--j)
				for(int k=1;~k;--k)
					for(int l=1;~l;--l)
						for(int a=j?x>>i&1:1;~a;--a)
							for(int b=k?y>>i&1:1;~b;--b)
								if(!l||(z>>i&1)<=(a^b)){
									u32*s=f[i][j&(x>>i&1)==a][k&(y>>i&1)==b][l&(z>>i&1)==(a^b)],*t=f[i+1][j][k][l];
									(s[0]+=t[0])%=p,(s[1]+=t[1]*2+(a^b)*t[0])%=p;
								}
		printf("%lld\n",(1[****f]-z%p******f%p+p)%p);
	}
}

4514: [Sdoi2016]数字配对

设f[x]为x的质因子指数和,考虑可以配对的a和b,设a<b,那么a|b且f[b]=f[a]+1。按f[x]的奇偶性分组建二分图,跑费用流。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll inf=-1e18;
const int N=205;
void eq1(int&a,int b){a=b<a?b:a;}
struct edge{
	int v,c;ll w;edge*s;
}e[N*N];
edge*p=e,*h[N];
void ins(int u,int v,ll w,int c){
	edge s={v,c,w,h[u]};
	edge t={u,0,-w,h[v]};
	*(h[u]=p++)=s;
	*(h[v]=p++)=t;
}
int n,s,t,q[N*N];
typedef int arr[N];
arr a,b,c,d,f,z;
ll r[N];
int dfs(int u,int c){
	if(u==t)return c;
	int f=0;
	for(edge*i=h[u];i;i=i->s)
		if(r[u]-i->w==r[i->v]&&d[u]-1==d[i->v]&&i->c){
			int j=dfs(i->v,min(c-f,i->c));
			i->c-=j,e[i-e^1].c+=j,f+=j;
			if(f==c)break;
		}
	if(!f)d[u]=-1;
	return f;
}
int spfa(){
	int f=0;
	ll c=0;
	while(1){
		fill(r+s,r+t,inf);
		q[0]=t;
		for(int a=0,b=0;a<=b;++a){
			int u=q[a];
			z[u]=0;
			for(edge*i=h[u];i;i=i->s)
				if(r[i->v]<r[u]-i->w&&e[i-e^1].c){
					r[i->v]=r[u]-i->w;
					d[i->v]=d[u]+1;
					if(!z[i->v]++)q[++b]=i->v;
				}
		}
		if(r[s]==inf)return f;
		while(1){
			int j=dfs(s,1e9);
			if(!j)break;
			if(r[s]<0&&c/-r[s]<=j){
				f+=c/-r[s],c%=-r[s];
				break;
			}
			f+=j,c+=j*r[s];
		}
	}
}
int main(){
	scanf("%d",&n),t=n+1;
	for(int i=1;i<=n;++i)
		scanf("%d",a+i);
	for(int i=1;i<=n;++i)
		scanf("%d",b+i);
	for(int i=1;i<=n;++i)
		scanf("%d",c+i);
	for(int i=1;i<=n;++i){
		int j=a[i];
		for(int d=2;d*d<=j;++d)
			for(;j%d==0;j/=d)++f[i];
		f[i]+=j!=1;
	}
	for(int i=1;i<=n;++i)
		if(f[i]&1){
			ins(s,i,0,b[i]);
			for(int j=1;j<=n;++j)
				if(abs(f[i]-f[j])==1&&max(a[i],a[j])%min(a[i],a[j])==0)
					ins(i,j,(ll)c[i]*c[j],1e9);
		}else
			ins(i,t,0,b[i]);
	printf("%d\n",spfa());
}

4515: [Sdoi2016]游戏

哪个sb把李超线段树出到树上的……修改拆成两条链,设t是s的祖先,那么x上的数字为a*(d[s]-d[x])+b,所以可以统一插入斜率为-a,截距为a*d[s]+b的线段。

#include<bits/stdc++.h>
#define I (i+j+2>>1)
#define J (i+j>>1)
#define P (k<<1)
#define S (k<<1^1)
using namespace std;
template<class T>
void eq1(T&a,T b){a=b<a?b:a;}
typedef long long ll;
const int N=1e5+5;
int n,n2;
typedef int arr[N];
arr d1,f1,f2,f3,f4,f5,f6;
ll d2[N],z[N*4];
struct tag{
	int s;ll t;
	ll operator()(int i){
		return s*d2[f6[i]]+t;
	}
}c[N*4];
void ins(tag f,int s,int t,int i,int j,int k){
	if(s<=i&&j<=t){
		if(i==j){
			if(f(i)<c[k](i))c[k]=f;
		}else
			if(f(i)<c[k](i))
				if(f(j)<c[k](j))c[k]=f;
				else
					if(f(J)>c[k](J))ins(f,s,t,i,J,P);
					else
						ins(c[k],s,t,I,j,S),c[k]=f;
			else
				if(f(j)<c[k](j))
					if(f(I)>c[k](I))ins(f,s,t,I,j,S);
					else
						ins(c[k],s,t,i,J,P),c[k]=f;
		eq1(z[k],c[k](i));
		eq1(z[k],c[k](j));
	}else{
		if(s<I)ins(f,s,t,i,J,P);
		if(t>J)ins(f,s,t,I,j,S);
		eq1(z[k],z[P]);
		eq1(z[k],z[S]);
	}
}
ll ask(int s,int t,int i,int j,int k){
	if(s==i&&j==t)return z[k];
	ll y=min(c[k](s),c[k](t));
	if(t<I)return min(y,ask(s,t,i,J,P));
	if(s>J)return min(y,ask(s,t,I,j,S));
	return min(y,min(ask(s,J,i,J,P),ask(I,t,I,j,S)));
}
struct edge{
	int v,w;edge*s;
}e[N*2];
edge*x=e,*h[N];
void ins(int u,int v,int w){
	edge s={v,w,h[u]};
	*(h[u]=x++)=s;
}
void dfs1(int u){
	f1[u]=1;
	for(edge*i=h[u];i;i=i->s)
		if(i->v!=f2[u]){
			f2[i->v]=u,d1[i->v]=d1[u]+1,d2[i->v]=d2[u]+i->w;
			dfs1(i->v);
			f1[u]+=f1[i->v];
			if(f1[i->v]>f1[f3[u]])
				f3[u]=i->v;
		}
}
void dfs2(int u,int v){
	f4[f6[f5[u]=++n2]=u]=v;
	if(f3[u])dfs2(f3[u],v);
	for(edge*i=h[u];i;i=i->s)
		if(i->v!=f2[u]&&i->v!=f3[u])
			dfs2(i->v,i->v);
}
int lca(int s,int t){
	while(f4[s]!=f4[t]){
		if(d1[f4[s]]<d1[f4[t]])swap(s,t);
		s=f2[f4[s]];
	}
	return d1[s]<d1[t]?s:t;
}
void ins(tag f,int s,int t){
	while(f4[s]!=f4[t]){
		ins(f,f5[f4[s]],f5[s],1,n,1);
		s=f2[f4[s]];
	}
	ins(f,f5[t],f5[s],1,n,1);
}
int main(){
	int m,w,u,v,s,t;
	scanf("%d%d",&n,&m);
	for(int i=2;i<=n;++i){
		scanf("%d%d%d",&u,&v,&w);
		ins(u,v,w),ins(v,u,w);
	}
	dfs1(1),dfs2(1,1);
	for(int i=1;i<N*4;++i)
		c[i].t=z[i]=123456789123456789;
	while(m--){
		scanf("%d%d%d",&w,&s,&t);
		if(w==1){
			scanf("%d%d",&u,&v);
			int l=lca(s,t);
			tag f1={-u,u*d2[s]+v},f2={u,u*d2[s]-u*d2[l]*2+v};
			ins(f1,s,l);
			ins(f2,t,l);
		}else{
			ll y=1e18;
			while(f4[s]!=f4[t]){
				if(d1[f4[s]]<d1[f4[t]])
					swap(s,t);
				y=min(y,ask(f5[f4[s]],f5[s],1,n,1));
				s=f2[f4[s]];
			}
			if(d1[s]<d1[t])swap(s,t);
			y=min(y,ask(f5[t],f5[s],1,n,1));
			printf("%lld\n",y);
		}
	}
}

4516: [Sdoi2016]生成魔咒

4517: [Sdoi2016]排列计数

裸的错排……

#include<cstdio>
typedef long long ll;
const int p=1e9+7;
const int N=1e6+5;
int q,n,m;
ll f1[N],f2[N],f3[N],f4[N];
int main(){
	f1[1]=f2[0]=f3[0]=f4[0]=1;
	for(int i=2;i<N;++i)
		f1[i]=f1[p%i]*(p-p/i)%p,f4[i]=(i-1)*(f4[i-1]+f4[i-2])%p;
	for(int i=1;i<N;++i)
		f2[i]=f2[i-1]*i%p,f3[i]=f3[i-1]*f1[i]%p;
	scanf("%d",&q);
	while(q--){
		scanf("%d%d",&n,&m);
		printf("%d\n",f2[n]*f3[m]%p*f3[n-m]%p*f4[n-m]%p);
	}
}

4518: [Sdoi2016]征途

斜率优化,设s为前缀和,容易推出$f_j+(m+1)s_j^2=2ms_is_j+f_i-(m-1)s_i^2$,横坐标和斜率都单调,单调队列维护即可。

#include<cstdio>
typedef long long ll;
const int N=3005;
int n,m,a,b,q[N];
ll s[N],f[N],g[N];
double cal1(int i,int j){
	return(f[i]-f[j]+(m+1)*(s[i]*s[i]-s[j]*s[j]))/1./(s[i]-s[j]);
}
ll cal2(int j,int i){
	return f[j]+(m-1)*s[i]*s[i]+(m+1)*s[j]*s[j]-m*s[i]*s[j]*2;
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i)
		scanf("%lld",s+i),s[i]+=s[i-1],f[i]=1e18;
	for(int z=1;z<=m;++z){
		q[a=b=0]=z-1;
		for(int i=z;i<=n;++i){
			while(a<b&&cal2(q[a],i)>cal2(q[a+1],i))
				++a;
			g[i]=cal2(q[a],i);
			while(a<b&&cal1(q[b],i)<cal1(q[b-1],i))
				--b;
			q[++b]=i;
		}
		for(int i=z;i<=n;++i)
			f[i]=g[i];
	}
	printf("%lld\n",f[n]);
}
时间: 2024-10-27 03:48:20

BZOJ4513~4518 SDOI2016 R1 题解的相关文章

bzoj-4518 4518: [Sdoi2016]征途(斜率优化dp)

题目链接: 4518: [Sdoi2016]征途 Description Pine开始了从S地到T地的征途. 从S地到T地的路可以划分成n段,相邻两段路的分界点设有休息站. Pine计划用m天到达T地.除第m天外,每一天晚上Pine都必须在休息站过夜.所以,一段路必须在同一天中走完. Pine希望每一天走的路长度尽可能相近,所以他希望每一天走的路的长度的方差尽可能小. 帮助Pine求出最小方差是多少. 设方差是v,可以证明,v×m^2是一个整数.为了避免精度误差,输出结果时输出v×m^2. In

动态规划(决策单调优化):BZOJ 4518 [Sdoi2016]征途

4518: [Sdoi2016]征途 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 532  Solved: 337[Submit][Status][Discuss] Description Pine开始了从S地到T地的征途. 从S地到T地的路可以划分成n段,相邻两段路的分界点设有休息站. Pine计划用m天到达T地.除第m天外,每一天晚上Pine都必须在休息站过夜.所以,一段路必须在同一天中走完. Pine希望每一天走的路长度尽可能相近,所以他

SDOI2016 R1 解题报告 bzoj4513~bzoj4518

储能表 将n, m分解为二进制,考虑一个log(n)层的trie树,n会在这颗trie树上走出了一个路径,因为 行数 $ \le n$,所以在n的二进制路径上,每次往1走的时候,与m计算贡献,m同样处理,$O(Tlog(n)log(m))$ 当然可以数位dp, $f_{i, n, m, k}$分别代表考虑到第i位,与n, m, k的大小关系,不是很好写,就写了上面的方法. 1 #include <bits/stdc++.h> 2 #define rep(i, a, b) for (int i

●BZOJ 4518 [Sdoi2016]征途

题链: http://www.lydsy.com/JudgeOnline/problem.php?id=4518 题解: 斜率优化DP 首先看看最后答案的形式: 设a[i]为第i天走的距离,那么 $ANS=\frac{\sum_{i=1}^{M}(a[i]-\overline{x})^2}{M}\times{M^2}$ $\;\qquad=\frac{(\sum_{i=1}^{M}a[i]^2)-2\overline{x}SUM+M\overline{x}^2}{M}\times{M^2}$ $

BZOJ 4518 [Sdoi2016]征途(分治DP)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=4518 [题目大意] 给出一个数列,分成m段,求方差最小,答案乘上m的平方. [题解] 化简式子可以发现,就是求将数列分成m段,最小化和的平方和.设dp[i][j]表示处理到第i段,已经用了前j个数的最小代价,我们可以得到dp[i][j]=min(dp[i-1][k]+(s[j]-s[k])2),由于决策单调,可以分治DP. [代码] #include <cstdio> typede

BZOJ4518:[SDOI2016]征途——题解

https://www.lydsy.com/JudgeOnline/problem.php?id=4518 https://www.luogu.org/problemnew/show/P4072 Pine开始了从S地到T地的征途. 从S地到T地的路可以划分成n段,相邻两段路的分界点设有休息站. Pine计划用m天到达T地.除第m天外,每一天晚上Pine都必须在休息站过夜.所以,一段路必须在同一天中走完. Pine希望每一天走的路长度尽可能相近,所以他希望每一天走的路的长度的方差尽可能小. 帮助P

【bzoj4513】[Sdoi2016]储能表 数位dp

我真TM是个sb!!! f[i][0/1][0/1][0/1]表示考虑到第i位前i位是否卡n的上界,是否卡m的上界,是否卡k的下界的数对的个数 g[i][0/1][0/1][0/1]表示考虑到第i位前i位是否卡n的上界,是否卡m的上界,是否卡k的下界的数对的和 直接dp #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<iostream> #

bzoj 4518 [Sdoi2016]征途 (斜率优化DP)

我犯了sb错误然后调了1个小时......队列写错了 斜率k递增,b取最小值,队列维护凸包即可 f[0]的预处理好像有些奇怪???我把inf调大就过了??? 1 #include <cstdio> 2 #include <algorithm> 3 #include <cstring> 4 #define il inline 5 #define ll long long 6 #define N 3010 7 #define inf 66666666 8 using nam

bzoj4518[Sdoi2016]征途 斜率优化dp

4518: [Sdoi2016]征途 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 1657  Solved: 915[Submit][Status][Discuss] Description Pine开始了从S地到T地的征途. 从S地到T地的路可以划分成n段,相邻两段路的分界点设有休息站. Pine计划用m天到达T地.除第m天外,每一天晚上Pine都必须在休息站过夜.所以,一段路必须在同一天中走完. Pine希望每一天走的路长度尽可能相近,所以