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. Windows Community Toolkit 4.0 - DataGrid - Part01

    概述 在上面一篇 Windows Community Toolkit 4.0 - DataGrid - Overview 中,我们对 DataGrid 控件做了一个概览的介绍,今天开始我们会做进一步的 ...

  2. python 操作数据库

    官方文档:https://www.python.org/dev/peps/pep-0249/ 1.创建connection,建立网络连接 MySQLdb.Connect(host,port,user, ...

  3. CISCO交换机-SNMP配置

    1.1     SNMP基础配置 router> enable 进入路由器是用户模式 router# conf terminal 进入路由器的全局配置模式 #snmp-server commun ...

  4. 通过 sass-resources-loader 全局注册 Sass/Less 变量

    使用webpack引入sass/less全局变量 sass或者less都提供变量设置,在需求切换主题的项目中使用less或者sass变量,只要修改变量值,编译后所有用到该变量的样式都会被修改为你想要的 ...

  5. PS制作墙壁上海报卷页图片效果

    1.首先,打开PS,新建合适的画布. 2.为了使背景具有质感,执行滤镜—滤镜库—纹理化,具体参数按你的感觉来. 3.新建画布“图层1”,为了方便观察,填充为灰色画布,ctrl+t适当缩小画布大小,如图 ...

  6. Dapper.NET

    关于Dapper.NET的相关论述   年少时,为何不为自己的梦想去拼搏一次呢?纵使头破血流,也不悔有那年少轻狂.感慨很多,最近事情也很多,博客也很少更新了,毕竟每个人都需要为自己的生活去努力. 最近 ...

  7. Jmeter之发送请求入参必须使用编码格式、Jmeter之发送Delete请求可能入参需要使用编码格式

    这里的其中一个属性值必须要先编码再传参才可以,具体可以通过抓包分析观察:

  8. 解决Window安全中心对Kitematic-0.17.3-Ubuntu.zip提示病毒,但无法删除的问题。

    Trojan:JS/Tisifi.B 类型:特洛伊木马 containerfile: C:\Users\Administrator\Desktop\Kitematic-0.17.3-Ubuntu.zi ...

  9. marMariaDB & MYSQL flexviews

    Using Flexviews - part one, introduction to materialized views - Percona Database Performance Bloght ...

  10. PhpStorm 配置链接远程虚拟机

    安装好了 PhpStorm 之后,打开项目文件夹,接着点击工具栏 Tools: 2.接着点击 tools>Deployment: 3.点击Configuration 开始配置    4.填好箭头 ...