码迷,mamicode.com
首页 > 其他好文 > 详细

CodeCraft-19 and Codeforces Round #537 Div. 2

时间:2019-02-07 00:12:00      阅读:240      评论:0      收藏:0      [点我收藏+]

标签:swap   names   etc   efi   固定   getchar   bsp   pen   stdin   

  D:即有不超过52种物品,求容量为n/2的有序01背包方案数。容易想到设f[i][j]为前i种物品已用容量为j的方案数,有f[i][j]=f[i-1][j-a[i]]*C(n/2-j+a[i],a[i])+f[i-1][j]*C(n/2-s[i-1]+j,a[i])。显然本质不同询问只有O(k2)种,暴力就是O(n·k3)的。

  考虑优化,询问相当于是把两个物品从背包中拿出,合并两物品后再放入背包。只要线性完成拿出物品的操作就可以优化到O(n·k2)。然而上面的式子并不能完成还原,因为后一部分的系数可能为0。但注意到进行背包的无序分配后,物品排列的方案数是固定的。所以之前的dp式子改为普通的01背包计数就可以还原了,最后再乘上排列方案数。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define N 100010
#define P 1000000007
#define M 55
char getc(){char c=getchar();while ((c<‘A‘||c>‘Z‘)&&(c<‘a‘||c>‘z‘)&&(c<‘0‘||c>‘9‘)) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
int read()
{
	int x=0,f=1;char c=getchar();
	while (c<‘0‘||c>‘9‘) {if (c==‘-‘) f=-1;c=getchar();}
	while (c>=‘0‘&&c<=‘9‘) x=(x<<1)+(x<<3)+(c^48),c=getchar();
	return x*f;
}
int n,m,q,f[N],g[N],a[M],sum[M],fac[N],inv[N],ans[M][M],tot;
char s[N];
int trans(char a){if (a<=‘Z‘) return a-‘A‘+1;else return a-‘a‘+27;}
int C(int n,int m){return 1ll*fac[n]*inv[m]%P*inv[n-m]%P;}
int INV_C(int n,int m){return 1ll*inv[n]*fac[m]%P*fac[n-m]%P;}
void inc(int &x,int y){x+=y;if (x>=P) x-=P;}
void ins(int u,int sum,int *f)
{
	if (!u) return;
	for (int j=min(sum,n);j>=0;j--)
	if (j>=u) inc(f[j],f[j-u]);
}
void del(int u,int sum)
{
	if (!u) return;
	for (int j=0;j<=min(sum,n);j++)
	if (j>=u) inc(g[j],P-g[j-u]);
}
int main()
{
#ifndef ONLINE_JUDGE
	freopen("d.in","r",stdin);
	freopen("d.out","w",stdout);
	const char LL[]="%I64d\n";
#endif
	scanf("%s",s+1);
	n=strlen(s+1);for (int i=1;i<=n;i++) a[trans(s[i])]++;
	fac[0]=1;for (int i=1;i<=n;i++) fac[i]=1ll*fac[i-1]*i%P;
	inv[0]=inv[1]=1;for (int i=2;i<=n;i++) inv[i]=P-1ll*(P/i)*inv[P%i]%P;
	for (int i=2;i<=n;i++) inv[i]=1ll*inv[i-1]*inv[i]%P;
	m=52;n>>=1;
	tot=1ll*fac[n]*fac[n]%P;
	for (int i=1;i<=m;i++) tot=1ll*tot*inv[a[i]]%P;
	for (int k=1;k<=m;k++)
	{
		memset(f,0,sizeof(f));f[0]=1;
		for (int i=1;i<=m;i++) sum[i]=sum[i-1]+(i!=k)*a[i];
		for (int i=1;i<=m;i++)
		if (i!=k) ins(a[i],sum[i],f);
		for (int j=k+1;j<=m;j++)
		{
			for (int i=0;i<=n;i++) g[i]=f[i];
			del(a[j],sum[m]);
			ins(a[j]+a[k],n*2,g);
			ans[k][j]=1ll*g[n]*C(a[j]+a[k],a[j])%P;
			ans[k][j]=1ll*fac[a[j]]*fac[a[k]]%P*inv[a[j]+a[k]]%P*ans[k][j]%P;
		}
		ins(a[k],n*2,f);
		ans[k][k]=f[n];
	}
	q=read();
	for (int i=1;i<=q;i++)
	{
		int x=trans(s[read()]),y=trans(s[read()]);
		if (x>y) swap(x,y);
		printf("%d\n",1ll*ans[x][y]*tot%P);
	}
	return 0;
}

  E:容易想到建出虚树,换根只要换个点开始dfs就行了。问题在于dp,传统树形dp的思路似乎很难做到O(nm),因为无法避开合并两个O(m)的状态(当然应该可以只是我不会)。考虑按dfs序dp,这样每一个点只要和他的祖先在不同集合即可,祖先两两之间显然也在不同集合,所以能放的集合个数是确定的,就非常显然了。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 100010
#define P 1000000007
#define M 310
#define ll long long
char getc(){char c=getchar();while ((c<‘A‘||c>‘Z‘)&&(c<‘a‘||c>‘z‘)&&(c<‘0‘||c>‘9‘)) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
int read()
{
	int x=0,f=1;char c=getchar();
	while (c<‘0‘||c>‘9‘) {if (c==‘-‘) f=-1;c=getchar();}
	while (c>=‘0‘&&c<=‘9‘) x=(x<<1)+(x<<3)+(c^48),c=getchar();
	return x*f;
}
int n,m,q,p[N],a[N],fac[N],inv[N],dfn[N],size[N],fa[N][20],cnt,deep[N],t;
struct data{int to,nxt;
}edge[N<<1];
void addedge(int x,int y){t++;edge[t].to=y,edge[t].nxt=p[x],p[x]=t;}
void inc(int &x,int y){x+=y;if (x>=P) x-=P;}
int ksm(int a,int k)
{
	int s=1;
	for (;k;k>>=1,a=1ll*a*a%P) if (k&1) s=1ll*s*a%P;
	return s;
}
int Inv(int a){return ksm(a,P-2);}
int C(int n,int m){return 1ll*fac[n]*inv[m]%P*inv[n-m]%P;}
int lca(int x,int y)
{
	if (deep[x]<deep[y]) swap(x,y);
	for (int j=19;~j;j--) if (deep[fa[x][j]]>=deep[y]) x=fa[x][j];
	if (x==y) return x;
	for (int j=19;~j;j--) if (fa[x][j]!=fa[y][j]) x=fa[x][j],y=fa[y][j];
	return fa[x][0];
}
int calc(int j,int x,int y){if (x+y<j) return 0;return 1ll*C(x,x+y-j)*C(y,x+y-j)%P*fac[x+y-j]%P;}
namespace virtual_tree
{
	int n,p[N],t,T,stk[N],top,f[2][M],g[M],x[N],y[N],DFN[N],DEEP[N],cnt;
	bool flag[N];
	struct data{int to,nxt;}edge[N<<1];
	void addedge(int u,int v){t++;x[t]=u,y[t]=v;}
	void Addedge(int x,int y){T++;edge[T].to=y,edge[T].nxt=p[x],p[x]=T;}
	bool cmp(const int&a,const int &b)
	{
		return dfn[a]<dfn[b];
	}
	bool cmp2(const int&a,const int &b)
	{
		return DFN[a]<DFN[b];
	}
	void build(int root,int m)
	{
		bool f=0;cnt=0;n=m;
		for (int i=1;i<=n;i++) if (a[i]==root) {f=1;break;} 
		if (!f) a[++n]=root;
		sort(a+1,a+n+1,cmp);
		stk[top=1]=1;t=0; 
		for (int i=1+(a[1]==1);i<=n;i++)
		{
			int l=lca(a[i],stk[top]);
			if (stk[top]!=l) 
			{
				while (top>1&&deep[stk[top-1]]>=deep[l]) addedge(stk[top-1],stk[top]),top--;
				if (stk[top]!=l) addedge(l,stk[top]);
				stk[top]=l;
			}
			stk[++top]=a[i];
		}
		while (top>1) addedge(stk[top-1],stk[top]),top--;
		for (int i=1;i<=t;i++) p[x[i]]=p[y[i]]=0;T=0;
		for (int i=1;i<=t;i++) Addedge(x[i],y[i]),Addedge(y[i],x[i]);
		for (int i=1;i<=t;i++) flag[x[i]]=flag[y[i]]=0;
		for (int i=1;i<=n;i++) if (a[i]!=root||f) flag[a[i]]=1;
		DEEP[root]=0;
	}
	void dfs(int k,int from)
	{
		DFN[k]=++cnt;
		for (int i=p[k];i;i=edge[i].nxt)
		if (edge[i].to!=from)
		{
			DEEP[edge[i].to]=DEEP[k]+flag[k];
			dfs(edge[i].to,k);
		}
	}
	void work(int root)
	{
		sort(a+1,a+n+1,cmp2);
		f[!flag[root]][0]=1;for (int j=1;j<=m;j++) f[!flag[root]][j]=0;
		for (int i=1+(!flag[root]);i<=n;i++)
		{
			f[i&1][0]=0;
			for (int j=1;j<=m;j++)
			{
				f[i&1][j]=f[i&1^1][j-1];
				if (j>=DEEP[a[i]]) inc(f[i&1][j],1ll*f[i&1^1][j]*(j-DEEP[a[i]])%P);
			}
		}
	}
}
void dfs(int k,int from)
{
	dfn[k]=++cnt;size[k]=1;
	for (int i=p[k];i;i=edge[i].nxt)
	if (edge[i].to!=from)
	{
		fa[edge[i].to][0]=k;
		deep[edge[i].to]=deep[k]+1;
		dfs(edge[i].to,k);
		size[k]+=size[edge[i].to];
	}
}
signed main()
{
#ifndef ONLINE_JUDGE
	freopen("aaa.in","r",stdin);
	freopen("aaa.out","w",stdout);
	const char LL[]="%I64d\n";
#endif
	n=read(),q=read();
	for (int i=1;i<n;i++)
	{
		int x=read(),y=read();
		addedge(x,y),addedge(y,x);
	}
	fac[0]=1;for (int i=1;i<=n;i++) fac[i]=1ll*fac[i-1]*i%P;
	inv[0]=inv[1]=1;for (int i=2;i<=n;i++) inv[i]=P-1ll*(P/i)*inv[P%i]%P;
	for (int i=2;i<=n;i++) inv[i]=1ll*inv[i-1]*inv[i]%P;
	fa[1][0]=1;dfs(1,1);
	for (int j=1;j<20;j++)
		for (int i=1;i<=n;i++)
		fa[i][j]=fa[fa[i][j-1]][j-1];
	for (int i=1;i<=q;i++)
	{
		int k=read();m=read();int root=read();
		for (int j=1;j<=k;j++) a[j]=read();
		virtual_tree::build(root,k);
		virtual_tree::dfs(root,root);
		virtual_tree::work(root);
		int ans=0;
		for (int j=0;j<=m;j++) inc(ans,virtual_tree::f[virtual_tree::n&1][j]);
		printf("%d\n",ans);
	}
	return 0;
	//NOTICE LONG LONG!!!!!
}

 

  

 

CodeCraft-19 and Codeforces Round #537 Div. 2

标签:swap   names   etc   efi   固定   getchar   bsp   pen   stdin   

原文地址:https://www.cnblogs.com/Gloid/p/10354276.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!