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. [翻译] 使用 Visual Studio 2019 来提高每个开发人员的工作效率

    [翻译] 使用 Visual Studio 2019 来提高每个开发人员的工作效率 原文: Making every developer more productive with Visual Stu ...

  2. Centos7修改系统时区

    timedatectl status Local time: 四 2014-12-25 10:52:10 CST Universal time: 四 2014-12-25 02:52:10 UTC R ...

  3. 1000/problem/A

    传送门: [http://codeforces.com/contest/1000/problem/A] 题意: 一个比赛颁奖,要准备T-Shirt给获奖者,但有的去年获奖过,衣服尺寸可以不改,有的需要 ...

  4. OSS网页上传和断点续传(终结篇)

    有了之前OSS网页上传和断点续传(OSS配置篇)和(STSToken篇),其万事俱备只欠东风啦,此终结篇即将展示OSS上传文件及断点续传的无限魅力... 网络卡顿.延迟能续传吗?能! 关了浏览器,还能 ...

  5. Shell脚本2

      5 Shell传递参数 我们可以在执行 Shell 脚本时,向脚本传递参数, 脚本内获取参数的格式为:$n.n 代表一个数字,1 为执行脚本的第一个参数,2 为执行脚本的第二个参数,以此类推…… ...

  6. Python之拆分目录

    成分目录的好习惯,使得代码保持整洁,为以后的代码管理提供方便. 一.概念 一般目录有以下几个: bin:程序入口,存放start文件. conf:存放固定的配置信息,比如:连接redis的配置信息.连 ...

  7. 便捷的ajax请求

    为什么要做这个呢?如果后端给的数据不单有JSON字符串,还有对象呢?这个时候我们就要每个都处理(JSON.parse).万一后端又改了,所有都是对象呢?如此一来我们就需要对我们的ajax进行封装. 这 ...

  8. JS刷新当前页面的几种方法总结

    reload 方法,该方法强迫浏览器刷新当前页面. 语法:location.reload([bForceGet]) 参数: bForceGet, 可选参数, 默认为 false,从客户端缓存里取当前页 ...

  9. Linux的基本解读

    Linux是一套免费使用和自由传播的类Unix操作系统,是一个基于POSIX和UNIX的多用户.多任务.支持多线程和多CPU的操作系统 而严格来讲,Linux这个词本身只表示Linux内核,但实际上人 ...

  10. Tomcat Windows 系统下安装及注意事项

    1 获取Tomcat 安装包  http://tomcat.apache.org/ tar.gz 文件是Linux系统下的安装版本 exe文件是 Windows系统下的安装版本 zip 文件是Wind ...