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 ...
随机推荐
- [翻译] ASP.NET Core 2.1.0 发布
原文: ASP.NET Core 2.1.0 now available 今天,我们很高兴可以发布 ASP.NET Core 2.1.0!这是我们 .NET平台下开源的.跨平台的 Web 框架的最新版 ...
- 聊聊阿里社招面试,谈谈“野生”Java程序员学习的道路
引言 很尴尬的是,这个类型的文章其实之前笔者就写过,原文章里,笔者自称LZ(也就是楼主,有人说是老子的简写,笔者只想说,这位同学你站出来,保证不打死你,-_-),原文章名称叫做<回答阿里社招面试 ...
- 关于eclipse tomcat 无法启动(8080,8005,8009端口被占用)的解决方法,附 eclipse tomcat 与 tomcat 并存方式
eclipse 在编译运行时 新建的tomcat连接始终为stopped状态,描述为8080,8005,8009端口被占用. 这是因为在装完tomcat后,tomcat服务已启动,而eclipse仅仅 ...
- Beta阶段团队成员贡献分分配规则
Beta阶段团队成员贡献分分配规则 Alpha阶段贡献分分配有些负责,在这里进行一些修改: 对任务完成得分部分进行了简化 对发现bug的惩罚措施进行了修改 移除了优化得分,不鼓励修改他人代码 移除了帮 ...
- centos7下zabbix安装与部署
1.Zabbix介绍 zabbix是一个基于WEB界面的提供分布式系统监视以及网络监视功能的企业级的开源解决方案. zabbix能监视各种网络参数,保证服务器系统的安全运营:并提供灵活的通知机制以让系 ...
- 同步和异步概念(由DZW前端框架引发的百度地图api无法加载问题总结)
首先概念: 在计算机领域,同步就是指一个进程在执行某个请求的时候,若该请求需要一段时间才能返回信息,那么这个进程将会一直等待下去,直到收到返回信息才继续执行下去:异步是指进程不需要一直等下去,而是继续 ...
- RabbitMQ 安装与使用
RabbitMQ 安装与使用 前言 吃多了拉就是队列,吃饱了吐就是栈 使用场景 对操作的实时性要求不高,而需要执行的任务极为耗时:(发送短信,邮件提醒,更新文章阅读计数,记录用户操作日志) 存在异 ...
- 本地项目托管到github上
一,步骤 1.在github上新建一个仓库 2.进入我的项目目录, git init //初始化本地仓库 3.git add . //把修改的代码提交到暂存区 4.git status 该命令会把你本 ...
- Jenkins deploy war to tomcat over https
ssl - HTTPS login with Spring Security redirects to HTTP - Stack Overflow https://stackoverflow.com/ ...
- 硬盘扩容9999T
win+r运行创建命令:subst H: d:\123说明:H指的是想要创建的盘符,d:\123是文件路径 删除命令subst H: d/说明 :H指的是已创建的盘符,/d指的是删除的意思 注意新盘符 ...