hgoi#20190813

T1-蛋糕

你有一块大小为\(n×m\)的蛋糕,你想把它切成\(1×1\)的小块。每次你可以选择横着或者竖着切一刀,把蛋糕切成两部分,这两部分再分别进行切割,直到全都变成\(1×1\)的小块。你想知道有多少种不同的切法(交换任意两块蛋糕的切割顺序算同一种方案),对\(1000000007\)取模。

解法

做法很多,n和m只有300的大小,可以记搜,也可以dp
dp做法就是对于所有切的组合算两块小蛋糕的答案贡献

ac代码

#include<bits/stdc++.h>
#define mod 1000000007 
#define ll long long
#define N 310
using namespace std;
ll n,m,dp[N][N];
void add(ll &a,ll b){a+=b;if(a>=mod)a-=mod;}
int main()
{
	freopen("cake.in","r",stdin),freopen("cake.out","w",stdout);
	scanf("%lld%lld",&n,&m),dp[1][1]=1;
	for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)
	{
		for(int k=1;k<i;k++)add(dp[i][j],dp[i-k][j]*dp[k][j]%mod);
		for(int k=1;k<j;k++)add(dp[i][j],dp[i][j-k]*dp[i][k]%mod);
	}
	printf("%lld\n",dp[n][m]);
	return 0;
}

T2-找钱

小L所在的L国由于没有普及移动支付,依然在大规模使用纸币。一共有\(n\)种面值的纸币,面值互不相同。一天小L去商店购买一个价格为\(X\)元的物品,他提前知道了自己手里和店员手里每种面值的纸币的数量,他想知道一共有多少种付钱-找钱的方式。两种付钱-找钱的方式不同,当且仅当存在一种面值,在两种方案中小L付出的该种面值的纸币数量不同或店员找的该种面值的纸币数量不同。此外,设小L付出的纸币面值总数为\(Y\),则小L付出的纸币中不能存在面值小于等于\(Y-X\)的纸币(不然就没有必要付这张纸币了)。

解法

考虑最基础的dp,dp[i][j]表示考虑到i及i以后的所有纸币,和为j的方案数
对于递钱\(dp[i][j]=\sum\limits_{k=0}^{b[i]}{dp[i+1][j-k×a[i]]}(j < X+a[i])\)
对于还钱\(dp[i][j]=\sum\limits_{k=0}^{c[i]}{dp[i+1][j-k×a[i]]}(j < max(a[i]))\)
这样的复杂度是\(1e3×1e4×1e4\),显然会T,用类似前缀和的方法优化,复杂度就降为了\(1e3×1e4\)

ac代码

#include<bits/stdc++.h>
#define mod 1000000007
#define N 10010
using namespace std;
int n,m,cnt1,cnt2,ans,a[N],b[N],c[N],s[N<<1],dp1[N<<1],dp2[N<<1];
void add(int &x,int y){x+=y;if(x>=mod)x%=mod;}
void del(int &x,int y){x-=y;if(x<0)x+=mod;}
void solve1()
{
	for(int i=n;i>=1;i--)
	{
		memset(s,0,sizeof(s)),s[0]=1;
		for(int k=a[i];k<m+a[i];k++)
		{
			s[k]=(s[k-a[i]]+dp1[k])%mod;
			if(k-a[i]*b[i]-a[i]>=0)
				del(s[k],dp1[k-a[i]*b[i]-a[i]]);
		}
		for(int k=0;k<m+a[i];k++)dp1[k]=s[k];
	}
}
void solve2()
{
	for(int i=n;i>=1;i--)
	{
		memset(s,0,sizeof(s)),s[0]=1;
		for(int k=a[i];k<10000;k++)
		{
			s[k]=(s[k-a[i]]+dp2[k])%mod;
			if(k-a[i]*c[i]-a[i]>=0) 
				del(s[k],dp2[k-a[i]*c[i]-a[i]]);
		}
		for(int k=0;k<10000;k++)dp2[k]=s[k];
	}
}
void get_ans(){for(int i=0;i<10000;i++)add(ans,1ll*dp1[m+i]*dp2[i]%mod);}
int main()
{
	freopen("deal.in","r",stdin),freopen("deal.out","w",stdout);
	scanf("%d%d",&n,&m),dp1[0]=dp2[0]=s[0]=1;
	for(int i=1;i<=n;i++)scanf("%d%d%d",&a[i],&b[i],&c[i]);
	solve1(),solve2(),get_ans(),printf("%d\n",ans);
	return 0;
}

T3-城镇

L国一共有\(N\)座城镇,开始时它们两两不连通。L国计划依次建造\(N-1\)条道路,把所有城镇连通起来。每建完一条道路,你需要回答这条道路所在连通块内距离最远的两座城镇之间的距离。两座城镇之间的距离定义为从一座走到另一座所需要经过的最少道路数。

解法

引理:合并两棵树,新的直径的端点一定是原来两树中直径的端点。我并不会证明,感性理解即可(
把整幅图看做森林,每次连接两个点,即合并两棵树,然后求新树的直径
合并时,我们把小树合到大树中,每次遍历一次小树,修改新的深度与倍增数组
然后枚举4个点间的距离,最大的就是直径
这样的复杂度是\(n\log{n^2}\)的。这个我会证明了(
最劣情况显然是合并两个大小为\(\frac n2\)的树,此时耗时1次\(n\log n\)
试想,两个大小为\(\frac n2\)的树最劣由4个大小为\(\frac n4\)的树组成,此时耗时2次\(\frac n2\log{\frac n2}\),不到\(n\log n\)
依次往下推,总耗时不超过\(\log n\)\(n\log n\)

ac代码

#include<bits/stdc++.h>
#define N 300010
#define K 21
using namespace std;
struct Node{int to, next;}e[N<<1];
int n,x,y,cnt,fa[N],p1[N],p2[N],head[N],dep[N],sz[N],f[N][K];
int rt,rt1,rt2,u1,u2,u3,u4,d1,d2,d3,d4,d5,d6,dis;
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
void add(int u,int v){e[++cnt]={v,head[u]},head[u]=cnt;}
void dfs(int x,int fa)
{
    dep[x]=dep[fa]+1,f[x][0]=fa;
    for(int i=1;i<K;i++)f[x][i]=f[f[x][i-1]][i-1];
    for(int i=head[x];i;i=e[i].next)if(e[i].to!=fa)dfs(e[i].to,x);
}
int lca(int x,int y)
{
    if(dep[x]!=dep[y])
	{
        if(dep[x]<dep[y])swap(x,y);
        for(int i=0;i<K;i++)if((dep[x]-dep[y])&(1<<i))x=f[x][i];
    }
    if(x==y)return x;
    for(int i=K-1;i>=0;i--)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
    return f[x][0];
}
int get_dis(int x,int y){return dep[x]+dep[y]-dep[lca(x,y)]*2;}
int main()
{
	freopen("town.in","r",stdin),freopen("town.out","w",stdout);
	scanf("%d",&n);
	for(int i=1;i<=n;i++)fa[i]=p1[i]=p2[i]=i,sz[i]=1;
    for(int i=1;i<n;i++)
	{
		scanf("%d%d",&x,&y),add(x,y),add(y,x),rt,rt1=find(x),rt2=find(y);
		if(sz[rt1]<sz[rt2])dfs(x,y),sz[rt2]+=sz[rt1],fa[rt1]=rt=rt2;
		else dfs(y,x),sz[rt1]+=sz[rt2],fa[rt2]=rt=rt1;
		u1=p1[rt1],u2=p2[rt1],u3=p1[rt2],u4=p2[rt2];
		d1=get_dis(u1,u2),d2=get_dis(u3,u4),d3=get_dis(u1,u3);
		d4=get_dis(u1,u4),d5=get_dis(u2,u3),d6=get_dis(u2,u4);
		dis=max(max(d1,d2),max(max(d3,d4),max(d5,d6)));
		if(d1==dis)p1[rt]=u1,p2[rt]=u2;if(d2==dis)p1[rt]=u3,p2[rt]=u4;
		if(d3==dis)p1[rt]=u1,p2[rt]=u3;if(d4==dis)p1[rt]=u1,p2[rt]=u4;
		if(d5==dis)p1[rt]=u2,p2[rt]=u3;if(d6==dis)p1[rt]=u2,p2[rt]=u4;
		printf("%d\n",dis);
    }
    return 0;
}
posted @ 2019-08-13 15:13  可耐滴小慕容  阅读(327)  评论(0编辑  收藏  举报