清华集训2016Day4

清华集训2016Day4

组合数问题(problem)

用卢卡斯定理可知满足条件即将\(n\)\(m\)分别用\(k\)进制表示,要求\(n\)的每一位都要大于等于\(m\)的对应位。直接数位\(dp\),设\(f_{i,0/1,0/1}\)表示处理到第\(i\)位,\(n\)是否触上界,\(m\)是否触上界时的方案数。复杂度\(O(t\log_kn)\)

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define ll long long
const ll mod = 1000000007;
int t,k,l,a[100],b[100];ll n,m,f[100][4];
int main(){
	freopen("problem.in","r",stdin);
	freopen("problem.out","w",stdout);
	scanf("%d%d",&t,&k);
	while (t--){
		scanf("%lld%lld",&n,&m);m=min(n,m);
		int all=((2*n-m+2)%mod)*((m+1)%mod)%mod*500000004%mod;
		memset(a,0,sizeof(a));memset(b,0,sizeof(b));
		l=0;while (m) b[++l]=m%k,m/=k;
		l=0;while (n) a[++l]=n%k,n/=k;
		f[0][0]=f[0][1]=f[0][2]=f[0][3]=1;
		for (int i=1;i<=l;++i){
			f[i][0]=(k*(k+1)>>1)*f[i-1][0]%mod;
			f[i][1]=((a[i]*(a[i]+1)>>1)*f[i-1][0]+(a[i]+1)*f[i-1][1])%mod;
			f[i][2]=((b[i]*(2*k-b[i]+1)>>1)*f[i-1][0]+(k-b[i])*f[i-1][2])%mod;
			if (b[i]<=a[i])
				f[i][3]=(f[i-1][3]+(a[i]-b[i])*f[i-1][2]+b[i]*f[i-1][1]+(b[i]*(2*a[i]-b[i]+1)>>1)*f[i-1][0])%mod;
			else
				f[i][3]=((a[i]+1)*f[i-1][1]+(a[i]*(a[i]+1)>>1)*f[i-1][0])%mod;
		}
		printf("%lld\n",(all-f[l][3]+mod)%mod);
	}
	return 0;
}

汽水(soda)

看见平均值自然可以想到分数规划。题目中所给的\(k\)可以减到每条边的权值里面去,这样问题就变成了找一条路径使其平均值的绝对值最小。

先点分,对于每个分治重心做一次二分。假设组成答案路径的是\((A,B),(C,D)\)这两个二元组,其中\(A,C\)代表两条到根路径的权值和,\(B,D\)代表长度。二分\(-k<\frac{A+C}{B+D}<k\),只需要判断是否存在满足条件的二元组即可。

先假设\(A+C\ge0\),那么就只需要满足\(\frac{A+C}{B+D}<k\)这个条件,即\(kB-A>C-kD\)。按\(A\)从小到大枚举\((A,B)(A\ge0)\),加入所有使\(A+C\ge0\)\((C,D)\),维护\(C-kD\)的最小值即可。考虑到\((A,B),(C,D)\)不能来自于分治重心的同一棵子树,故需要维护来自不同子树的最小及次小值。

\(A+C<0\)同理,可得\(A+kB>-C-kD\),故维护\(-C-kD\)的最小值即可。

这样复杂度为\(O(n\log^2n)\)

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define ll long long
#define pi pair<ll,int>
#define mk make_pair
const int N = 50005;
int n,to[N<<1],nxt[N<<1],head[N],cnt,sz[N],w[N],vis[N],root,sum,top,pos;
ll ww[N<<1],ans=1ll<<60;
struct data{
	ll x,y,z;
	data(){x=y=z=0;}
	data(ll _x,ll _y,ll _z){x=_x,y=_y,z=_z;}
	bool operator < (const data &b) const {return y<b.y;}
}s[N];
pi m1,m2;
void getroot(int u,int f){
	sz[u]=1;w[u]=0;
	for (int e=head[u];e;e=nxt[e])
		if (to[e]!=f&&!vis[to[e]]){
			getroot(to[e],u),sz[u]+=sz[to[e]];
			w[u]=max(w[u],sz[to[e]]);
		}
	w[u]=max(w[u],sum-sz[u]);
	if (w[u]<w[root]) root=u;
}
void dfs(int u,int f,ll dep,ll dis,int ac){
	s[++top]=data(dep,dis,ac);
	for (int e=head[u];e;e=nxt[e])
		if (to[e]!=f&&!vis[to[e]])
			dfs(to[e],u,dep+1,dis+ww[e],ac);
}
void upt(pi S){
	if (S.first<m1.first){
		if (S.second!=m1.second) m2=m1;
		m1=S;
	}else if (S.first<m2.first&&S.second!=m1.second) m2=S;
}
bool chk1(ll k){
	m1=m2=mk(1ll<<60,0);
	for (int i=pos,j=pos-1;i<=top;++i){
		while (j&&s[i].y+s[j].y>=0) upt(mk(s[j].y-k*s[j].x,s[j].z)),--j;
		if (k*s[i].x-s[i].y>(s[i].z==m1.second?m2.first:m1.first)) return true;
		upt(mk(s[i].y-k*s[i].x,s[i].z));
	}
	return false;
}
bool chk2(ll k){
	m1=m2=mk(1ll<<60,0);
	for (int i=pos-1,j=pos;i;--i){
		while (j<=top&&s[i].y+s[j].y<0) upt(mk(-k*s[j].x-s[j].y,s[j].z)),++j;
		if (k*s[i].x+s[i].y>(s[i].z==m1.second?m2.first:m1.first)) return true;
		upt(mk(-k*s[i].x-s[i].y,s[i].z));
	}
	return false;
}
void solve(int u){
	vis[u]=1;s[top=1]=data(0,0,0);
	for (int e=head[u];e;e=nxt[e])
		if (!vis[to[e]]) dfs(to[e],u,1,ww[e],to[e]);
	sort(s+1,s+top+1);
	for (pos=1;s[pos].y<0;++pos) ;
	ll l=1,r=ans;
	while (l<=r){
		ll mid=l+r>>1;
		if (chk1(mid)||chk2(mid)) ans=r=mid-1;
		else l=mid+1;
	}
	for (int e=head[u];e;e=nxt[e])
		if (!vis[to[e]]){
			root=0,sum=sz[to[e]];
			getroot(to[e],u),solve(root);
		}
}
int main(){
	freopen("soda.in","r",stdin);
	freopen("soda.out","w",stdout);
	ll k;scanf("%d%lld",&n,&k);
	for (int i=1;i<n;++i){
		int u,v;ll w;scanf("%d%d%lld",&u,&v,&w);w-=k;ans=min(ans,abs(w));
		to[++cnt]=v;nxt[cnt]=head[u];ww[cnt]=w;head[u]=cnt;
		to[++cnt]=u;nxt[cnt]=head[v];ww[cnt]=w;head[v]=cnt;
	}
	sum=w[0]=n;getroot(1,0);solve(1);
	printf("%lld\n",ans);return 0;
}

定向越野(circle)

咕咕咕

posted @ 2018-12-01 20:07  租酥雨  阅读(555)  评论(0编辑  收藏  举报