CodeCraft-19 and Codeforces Round #537 Div. 2
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的更多相关文章
- CodeCraft-19 and Codeforces Round #537 (Div. 2) E 虚树 + 树形dp(新坑)
https://codeforces.com/contest/1111/problem/E 题意 一颗有n个点的树,有q个询问,每次从树挑出k个点,问将这k个点分成m组,需要保证在同一组中不存在一个点 ...
- CodeCraft-19 and Codeforces Round #537 (Div. 2) D 多重排列 + 反向01背包 + 离线处理
https://codeforces.com/contest/1111/problem/D 多重排列 + 反向01背包 题意: 给你一个字符串(n<=1e5,n为偶数),有q个询问,每次询问两个 ...
- CodeCraft-19 and Codeforces Round #537 (Div. 2) 题解
传送门 D. Destroy the Colony 首先明确题意:除了规定的两种(或一种)字母要在同侧以外,其他字母也必须在同侧. 发现当每种字母在左/右边确定之后,方案数就确定了,就是分组的方案数乘 ...
- 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 ...
- 【CodeCraft-19 and Codeforces Round #537 (Div. 2) C】Creative Snap
[链接] 我是链接,点我呀:) [题意] 横坐标1..2^n对应着2^n个复仇者的基地,上面有k个复仇者(位置依次给出). 你是灭霸你要用以下方法消灭这k个复仇者: 一开始你获取整个区间[1..2^n ...
- Codeforces Round #354 (Div. 2) ABCD
Codeforces Round #354 (Div. 2) Problems # Name A Nicholas and Permutation standard input/out ...
- Codeforces Round 542 (Div. 2)
layout: post title: Codeforces Round 542 (Div. 2) author: "luowentaoaa" catalog: true tags ...
- Codeforces Round #456 (Div. 2)
Codeforces Round #456 (Div. 2) A. Tricky Alchemy 题目描述:要制作三种球:黄.绿.蓝,一个黄球需要两个黄色水晶,一个绿球需要一个黄色水晶和一个蓝色水晶, ...
- Codeforces Round #401 (Div. 2) 离翻身就差2分钟
Codeforces Round #401 (Div. 2) 很happy,现场榜很happy,完全将昨晚的不悦忘了.终判我校一片惨白,小董同学怒怼D\E,离AK就差一个C了,于是我AC了C题还剩35 ...
随机推荐
- Windows Community Toolkit 4.0 - DataGrid - Part02
概述 在上面一篇 Windows Community Toolkit 4.0 - DataGrid - Part01 中,我们针对 DataGrid 控件的 CollectionView 部分做了详细 ...
- webpack之loader和plugin简介
webpack之loader和plugin简介 webpack入门和实战(二):全面理解和运用loader和plugins webpack入门(四)——webpack loader 和plugin w ...
- sql中return和returns的区别
用户定义函数中,用RETURNS 子句指定该函数返回值的数据类型 return用于返回具体的值/值变量
- mysql_建表
建表 某字段多个默认值 创建表时,对多选多的字段设置两个以上的默认值时,两个默认值之间用逗号','隔开,不能加空格. create table student2( id int not null, n ...
- linux下编译upx ucl
昨天,UPX发布了3.93版本. UPX(the Ultimate Packer for eXecutables)是一个非常全面的可执行文件压缩软件,支持dos/exe.dos/com.dos/sys ...
- 通过arcmap发布缓存服务,无法选择自定义方案
出现该问题是因为缓存目录有该缓存信息,清楚掉之后就可以选择自定义方案了
- HDU 2459 Maximum repetition substring
题目:Maximum repetition substring 链接:http://acm.hdu.edu.cn/showproblem.php?pid=2459 题意:给你一个字符串,求连续重复出现 ...
- PhpStorm 头部注释、类注释和函数注释的设置
*设置位置:"Settings"->"file templates"; 如下图,设置头部注释.类注释以及函数注释,时间.用户名.文件名称等随机改变的属性, ...
- 使用npm安装一些包失败了,更换npm源
镜像使用方法(三种办法任意一种都能解决问题,建议使用第三种,将配置写死,下次用的时候配置还在): 1.通过config命令 npm config set registry https://regist ...
- 记一次ntp反射放大ddos攻击
2018/3/26 ,共计310G左右的DDoS攻击 临时解决办法:将web服务转移到同生产一个内网段的备份服务器a上,a提供web端口80,数据库通过内网连接还是沿用生产数据库. 后续解决办法:通过 ...