8.28
06:12 Graph Problem With Small
无向图上的状压dp
学到了一些东西:
1.通过先枚举状态然后枚举起点终点来转移,这样你有的时候会发现一些可以省掉的枚举
2.利用bitset来转移,看代码:
void Dp(){
int u=0;
dp[1][0]=1;
F(sta,0,(1<<n)-1){
if(disj(sta,1<<u))continue;
F(v,0,n-1){
if(v==u||disj(sta,1<<v))continue;
bitset<N>tmp=dp[sta^(1<<v)]&e[v];
if(tmp.count()!=0)dp[sta][v]=1;
}
}
F(s,0,(1<<n)-1){
int t=(((1<<n)-1)-s)|1;
F(v,0,n-1)
if(dp[s][v])ans[v]|=dp[t];
}
}
16:19 A Simple Task
这个也是的
然后这题的trick:
1.为了防止环记录重复,我们钦定最小的那个点为环的起点。
其次是注意一条边会被当做环记录,以及环会被正反两个方向统计,注意剪掉。
13:53 树上数颜色
树上启发式合并
首先,考虑暴力,我们对一个点的子树完全遍历,然后统计答案再减去,防止对其他的统计有影响
这个有一个性质,就是最后的一个子树答案不用剪掉
这个就是启发式合并复杂度正确的关键了
对树进行轻重剖分,每次先统计轻子节点的答案,然后减掉,对重儿子就不用减去
轻儿子只有log个,重儿子只走一遍,复杂度就对了
树上启发式合并就这了
15:48 点分树 | 震波
这个核工业代码
有几个代码实现上的关键点:
1.虚树上的点和实际上的点没有位置关系上的关联,所以说会有很多想当然上的问题
2.距离存在0,用BIT维护的时候要+1
3.每个节点都要开BIT,所以说空间会炸,需要基于vector的动态开点BIT.
4.注意两种BIT的空间的不同
然后是简要的做题思路
维护一颗基于淀粉质的虚树,显然树高是log的。
然后是求答案:
对于所有父子节点对\(i\)\(i和fa[i]有答案是fa[i]的- i的\)
这个具体的看代码?
void upd(int x,int k){
tr1[x].upd(0,k);
for(int i=x;fa[i];i=fa[i]){
tr1[fa[i]].upd(dist(fa[i],x),k);
tr2[i].upd(dist(fa[i],x),k);
}
}
int que(int x,int k){
int ans=0;
ans+=tr1[x].sum(k);
for(int i=x;fa[i];i=fa[i]){
int tmp=k-dist(fa[i],x);if(tmp<0)continue;
ans+=tr1[fa[i]].sum(tmp);
ans-=tr2[i].sum(tmp);
}
return ans;
}
18:28 大数
关键就是一个式子的转化:
\[\frac{sum[L]-sum[R+1]}{10^{n-R}} == 0(mod p)
\]
这个就要分类讨论了:
1.p不能整除10,也就是说 $$sum[L]-sum[R+1]==0$$
那么就是 $$sum[L]==sum[R+1]$$,变成了一般的莫队计数
2.否则,\(p==2 or 5\) 这也有很好的性质.\(相当于是你可以直接根据最后一位推断能否整除\)
根据以下公式直接计算:
F(i,1,n){
if((s[i]-'0')%p==0)s1[i]++,s2[i]+=i;
s1[i]+=s1[i-1];s2[i]+=s2[i-1];
}
m=rd();
while(m--){
int l=rd(),r=rd() ;
int num=s1[r]-s1[l-1],sum=s2[r]-s2[l-1];
cout<<sum-(l-1)*num<<'\n';
}
至于为什么是这个式子就自己手推一下就好了.

浙公网安备 33010602011771号