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的更多相关文章

  1. CodeCraft-19 and Codeforces Round #537 (Div. 2) E 虚树 + 树形dp(新坑)

    https://codeforces.com/contest/1111/problem/E 题意 一颗有n个点的树,有q个询问,每次从树挑出k个点,问将这k个点分成m组,需要保证在同一组中不存在一个点 ...

  2. CodeCraft-19 and Codeforces Round #537 (Div. 2) D 多重排列 + 反向01背包 + 离线处理

    https://codeforces.com/contest/1111/problem/D 多重排列 + 反向01背包 题意: 给你一个字符串(n<=1e5,n为偶数),有q个询问,每次询问两个 ...

  3. CodeCraft-19 and Codeforces Round #537 (Div. 2) 题解

    传送门 D. Destroy the Colony 首先明确题意:除了规定的两种(或一种)字母要在同侧以外,其他字母也必须在同侧. 发现当每种字母在左/右边确定之后,方案数就确定了,就是分组的方案数乘 ...

  4. CodeCraft-19 and Codeforces Round #537 (Div. 2) C. Creative Snap 分治

    Thanos wants to destroy the avengers base, but he needs to destroy the avengers along with their bas ...

  5. 【CodeCraft-19 and Codeforces Round #537 (Div. 2) C】Creative Snap

    [链接] 我是链接,点我呀:) [题意] 横坐标1..2^n对应着2^n个复仇者的基地,上面有k个复仇者(位置依次给出). 你是灭霸你要用以下方法消灭这k个复仇者: 一开始你获取整个区间[1..2^n ...

  6. Codeforces Round #354 (Div. 2) ABCD

    Codeforces Round #354 (Div. 2) Problems     # Name     A Nicholas and Permutation standard input/out ...

  7. Codeforces Round 542 (Div. 2)

    layout: post title: Codeforces Round 542 (Div. 2) author: "luowentaoaa" catalog: true tags ...

  8. Codeforces Round #456 (Div. 2)

    Codeforces Round #456 (Div. 2) A. Tricky Alchemy 题目描述:要制作三种球:黄.绿.蓝,一个黄球需要两个黄色水晶,一个绿球需要一个黄色水晶和一个蓝色水晶, ...

  9. Codeforces Round #401 (Div. 2) 离翻身就差2分钟

    Codeforces Round #401 (Div. 2) 很happy,现场榜很happy,完全将昨晚的不悦忘了.终判我校一片惨白,小董同学怒怼D\E,离AK就差一个C了,于是我AC了C题还剩35 ...

随机推荐

  1. Item 13: 比起iterator优先使用const_iterator

    本文翻译自modern effective C++,由于水平有限,故无法保证翻译完全正确,欢迎指出错误.谢谢! 博客已经迁移到这里啦 STL中的const_iterator等价于pointers-to ...

  2. Intellij IDEA创建的Web项目配置Tomcat并启动Maven项目

    本篇博客讲解IDEA如何配置Tomcat. 大部分是直接上图哦. 点击如图所示的地方,进行添加Tomcat配置页面 弹出页面后,按照如图顺序找到,点击+号 tomcat Service -> L ...

  3. [WPF]如何使用代码创建DataTemplate(或者ControlTemplate)

    1. 前言 上一篇文章([UWP]如何使用代码创建DataTemplate(或者ControlTemplate))介绍了在UWP上的情况,这篇文章再稍微介绍在WPF上如何实现. 2. 使用Framew ...

  4. LeetCode 905. Sort Array By Parity

    905. Sort Array By Parity Given an array A of non-negative integers, return an array consisting of a ...

  5. C#代码中插入X86汇编

    这两天在看C# SIMD相关的东西, 在爆栈上面搜到一段代码, 表示很震惊, 还是得贴出来… [UnmanagedFunctionPointer(CallingConvention.StdCall)] ...

  6. IIS6下使用多域名和通配符证书

    由于SSL协议,在完成握手以前,都只能采用IP地址通信方式,没有办法获取访问地址中的域名信息,所以针对每个IP地址的每个端口,服务器只能返回相同的一张证书.如果要实现多个不同域名共享一个IP地址的缺省 ...

  7. matplotlib 入门之Usage Guide

    文章目录 Usage Guide plotting函数的输入 matplotlib, pyplot, pylab, 三者的联系 Coding style Backends 后端 matplotlib教 ...

  8. Daily Scrum 12.19

    Member Task on 12.19 Task on 12.20 仇栋民 请假 完成Task972 : 完成活动评分基础功能 康家华 完成 Task1004 : 百度map UI优化 完成Task ...

  9. p9半幺群

    如何不理解划红线的地方?第二个划红线地方,请举一个例子 1.0不是幺元 2.f(1)=2, f(2)=1, f(3)=3, g(1)=2, g(2)=3, g(3)=1  fg不等于gf

  10. 01-学习vue前的准备工作

    起步 1.扎实的HTML/CSS/Javascript基本功,这是前置条件. 2.不要用任何的构建项目工具,只用最简单的<script>,把教程里的例子模仿一遍,理解用法.不推荐上来就直接 ...