A. 数数

排好序从两头贪心即可


B. 数树

首先很容易想到容斥

如果选择的边集的相关点集有点的度数大于 \(1\) 是不合法的

也就是说一定形成若干条长度不一的链

要给这些链上的点安排排列中的数,方案数其实就是 \((n-k)!\)

因为一条链开头的值确定了整条链的值就确定了

发现暴力算是 \(2^n\),考虑选择边集数量一定时贡献是否可以一起算

树形背包即可,算出以 \(1\) 为根的子树内选 \(k\) 条边的方案数

由于入度出度不超过 \(1\) 的限制,\(dp\) 加两维 \(0/1\) 表示有没有出/入边

代码实现
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=5005;
const int maxm=10005;
const int mod=998244353;
int n,hd[maxn],cnt,f[maxn][maxn][2][2],ans,fa[maxn],x,y,frac[maxn],siz[maxn],g[maxn][2][2];
int read(){
int x=0,f=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')f=-1;
ch=getchar();
}
while(isdigit(ch)){
x=x*10+ch-48;
ch=getchar();
}
return x*f;
}
struct Edge{
int nxt,to,val;
}edge[maxm];
void add(int u,int v,int w){
edge[++cnt].nxt=hd[u];
edge[cnt].to=v;
edge[cnt].val=w;
hd[u]=cnt;
return ;
}
void dfs(int u){
siz[u]=1;
f[u][0][0][0]=1;
for(int i=hd[u];i;i=edge[i].nxt){
int v=edge[i].to;
if(v==fa[u])continue;
fa[v]=u;
dfs(v);
// memset(g,0,sizeof g);
for(int j=siz[u];j>=0;j--){
for(int k=siz[v];k>=0;k--){
int sum=(f[v][k][0][0]+f[v][k][0][1]+f[v][k][1][0]+f[v][k][1][1])%mod;
for(int l=0;l<2;l++){
for(int r=0;r<2;r++){
g[j+k][l][r]=(g[j+k][l][r]+f[u][j][l][r]*sum%mod)%mod;
}
}
if(edge[i].val){
g[j+k+1][1][0]=(g[j+k+1][1][0]+f[u][j][0][0]*(f[v][k][0][0]+f[v][k][1][0])%mod)%mod;
g[j+k+1][1][1]=(g[j+k+1][1][1]+f[u][j][0][1]*(f[v][k][0][0]+f[v][k][1][0])%mod)%mod;
}
else{
g[j+k+1][0][1]=(g[j+k+1][0][1]+f[u][j][0][0]*(f[v][k][0][0]+f[v][k][0][1])%mod)%mod;
g[j+k+1][1][1]=(g[j+k+1][1][1]+f[u][j][1][0]*(f[v][k][0][0]+f[v][k][0][1])%mod)%mod;
}
}
}
siz[u]+=siz[v];
for(int j=0;j<=siz[u];j++){
for(int l=0;l<2;l++){
for(int r=0;r<2;r++){
f[u][j][l][r]=g[j][l][r];
g[j][l][r]=0;
}
}
}
}
return ;
}
signed main(){
n=read();
for(int i=1;i<=n-1;i++){
x=read();
y=read();
add(x,y,0);
add(y,x,1);
}
dfs(1);
frac[0]=1;
for(int i=1;i<=n;i++)frac[i]=frac[i-1]*i%mod;
int op=1;
for(int i=0;i<=n-1;i++){
int sum=0;
for(int j=0;j<=1;j++){
for(int k=0;k<=1;k++){
// cout<<f[1][i][j][k]<<" ";
sum=(sum+f[1][i][j][k])%mod;
}
}
// cout<<endl;
// cout<<sum<<endl;
ans=(ans+op*sum*frac[n-i]%mod)%mod;
ans=(ans+mod)%mod;
op=-op;
}
cout<<ans;
return 0;
}

C. 鼠树

发现单个白点的权值由其归属点的累加可以得出,子树和操作是子树内黑点总和以及一部分白点由其归属点得出

所以只需要维护黑点信息即可

记录每个黑点的权值,管辖点的个数,以及二者的乘积

当加入一个黑点的时候,其管辖点大小由子树内大小之和推出,权值设为归属点权值,并相应地更改归属点大小

当删除一个黑点的时候,由于要保留贡献,所以直接对这棵子树做区间加,并做一次 \(4\) 操作把子树内其他黑点多算的部分减掉

最后是动态维护归属点的问题:可以树剖并在每条重链上开 \(set\) 维护黑点深度,查询时在重链上二分即可

整个算法时间复杂度 \(nlogn\)

代码实现
#include<bits/stdc++.h>
using namespace std;
#define int unsigned int
const int maxn=3e5+5;
const int maxm=3e5+5;
int n,m,fa[maxn],hd[maxn],cnt,son[maxn],siz[maxn],id[maxn],re[maxn],tp[maxn],tot,x,y,w,op,dep[maxn],c[maxn],c1[maxn];
bool col[maxn];
set<int>s[maxn];
int read(){
int x=0,f=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')f=-1;
ch=getchar();
}
while(isdigit(ch)){
x=x*10+ch-48;
ch=getchar();
}
return x*f;
}
struct Edge{
int nxt,to;
}edge[maxm];
void add_edge(int u,int v){
edge[++cnt].nxt=hd[u];
edge[cnt].to=v;
hd[u]=cnt;
return ;
}
void dfs(int u){
siz[u]=1;
for(int i=hd[u];i;i=edge[i].nxt){
int v=edge[i].to;
if(v==fa[u])continue;
dep[v]=dep[u]+1;
fa[v]=u;
dfs(v);
siz[u]+=siz[v];
if(siz[v]>siz[son[u]])son[u]=v;
}
return ;
}
void dfs1(int u,int top){
tp[u]=top;
id[u]=++tot;
re[tot]=u;
if(son[u])dfs1(son[u],top);
for(int i=hd[u];i;i=edge[i].nxt){
int v=edge[i].to;
if(v==son[u]||v==fa[u])continue;
dfs1(v,v);
}
return ;
}
void add(int x,int w){
for(;x<=n;x+=x&-x)c[x]+=w;
}
void add1(int x,int w){
for(;x<=n;x+=x&-x)c1[x]+=w;
}
int ask(int x){
int ans=0;
for(;x;x-=x&-x)ans+=c[x];
return ans;
}
int ask1(int x){
int ans=0;
for(;x;x-=x&-x)ans+=c1[x];
return ans;
}
void change(int l,int r,int w){
add(l,w);
add(r+1,-w);
add1(l,w*(1-l));
add1(r+1,w*(l-1));
add1(r+1,w*(r-l+1));
return ;
}
int query(int l,int r){
return ask(r)*r+ask1(r)-ask(l-1)*(l-1)-ask1(l-1);
}
int get_t(int x){
while(1){
if(!s[tp[x]].empty()){
if(*s[tp[x]].begin()<=dep[x]){
set<int>::iterator it=s[tp[x]].upper_bound(dep[x]);
it--;
return re[id[x]-(dep[x]-(*it))];
}
}
x=fa[tp[x]];
}
return 0;
}
struct seg{
int l,r,siz,val,sum,lazy;
}t[maxn*4];
void build(int p,int l,int r){
t[p].l=l;
t[p].r=r;
if(l==r)return ;
int mid=l+r>>1;
build(p<<1,l,mid);
build(p<<1|1,mid+1,r);
return ;
}
void update(int p){
t[p].siz=t[p<<1].siz+t[p<<1|1].siz;
t[p].val=t[p<<1].val+t[p<<1|1].val;
t[p].sum=t[p<<1].sum+t[p<<1|1].sum;
return ;
}
void dospread(int p,int w){
t[p].val+=w;
t[p].sum+=w*t[p].siz;
t[p].lazy+=w;
return ;
}
void spread(int p){
dospread(p<<1,t[p].lazy);
dospread(p<<1|1,t[p].lazy);
t[p].lazy=0;
return ;
}
void change_val(int p,int l,int r,int w){
if(l>r)return ;
if(t[p].l>=l&&t[p].r<=r){
dospread(p,w);
return ;
}
if(t[p].lazy)spread(p);
int mid=t[p].l+t[p].r>>1;
if(l<=mid)change_val(p<<1,l,r,w);
if(r>mid)change_val(p<<1|1,l,r,w);
update(p);
return ;
}
void change_siz(int p,int pos,int w){
if(t[p].l==t[p].r&&t[p].l==pos){
t[p].siz+=w;
t[p].sum+=w*t[p].val;
return ;
}
if(t[p].lazy)spread(p);
int mid=t[p].l+t[p].r>>1;
if(pos<=mid)change_siz(p<<1,pos,w);
else change_siz(p<<1|1,pos,w);
update(p);
return ;
}
int ask_val(int p,int pos){
if(t[p].l==t[p].r&&t[p].l==pos){
return t[p].val;
}
if(t[p].lazy)spread(p);
int mid=t[p].l+t[p].r>>1;
if(pos<=mid)return ask_val(p<<1,pos);
return ask_val(p<<1|1,pos);
}
int ask_siz(int p,int l,int r){
if(l>r)return 0;
if(t[p].l>=l&&t[p].r<=r)return t[p].siz;
if(t[p].lazy)spread(p);
int mid=t[p].l+t[p].r>>1,ans=0;
if(l<=mid)ans=ask_siz(p<<1,l,r);
if(r>mid)ans+=ask_siz(p<<1|1,l,r);
return ans;
}
int ask_sum(int p,int l,int r){
if(l>r)return 0;
if(t[p].l>=l&&t[p].r<=r)return t[p].sum;
if(t[p].lazy)spread(p);
int mid=t[p].l+t[p].r>>1,ans=0;
if(l<=mid)ans=ask_sum(p<<1,l,r);
if(r>mid)ans+=ask_sum(p<<1|1,l,r);
return ans;
}
void cover(int p,int pos,int w){
if(t[p].l==t[p].r&&t[p].l==pos){
t[p].val=w;
t[p].sum=w*t[p].siz;
return ;
}
if(t[p].lazy)spread(p);
int mid=t[p].l+t[p].r>>1;
if(pos<=mid)cover(p<<1,pos,w);
else cover(p<<1|1,pos,w);
update(p);
return ;
}
void rev(int x){
if(col[x]){
col[x]=false;
int cnt=siz[x]-ask_siz(1,id[x]+1,id[x]+siz[x]-1);
int y=get_t(fa[x]);
s[tp[x]].erase(dep[x]);
int val1=ask_val(1,id[x]);
int val2=ask_val(1,id[y]);
change_siz(1,id[x],-cnt);
cover(1,id[x],0);
change_siz(1,id[y],cnt);
change(id[x],id[x]+siz[x]-1,val1-val2);
change_val(1,id[x],id[x]+siz[x]-1,val2-val1);
}
else{
col[x]=true;
int cnt=siz[x]-ask_siz(1,id[x]+1,id[x]+siz[x]-1);
int y=get_t(x);
s[tp[x]].insert(dep[x]);
int val2=ask_val(1,id[y]);
change_siz(1,id[x],cnt);
change_siz(1,id[y],-cnt);
cover(1,id[x],val2);
}
return ;
}
signed main(){
n=read();
m=read();
for(int i=2;i<=n;i++){
fa[i]=read();
add_edge(fa[i],i);
}
dep[1]=1;
dfs(1);
dfs1(1,1);
build(1,1,n);
col[1]=true;
change_siz(1,id[1],siz[1]);
s[1].insert(1);
while(m--){
op=read(),x=read();
if(op==1){
printf("%u\n",ask_val(1,id[get_t(x)])+query(id[x],id[x]));
}
if(op==2){
w=read();
change_val(1,id[x],id[x],w);
}
if(op==3){
int ans=ask_val(1,id[get_t(x)])*(siz[x]-ask_siz(1,id[x]+1,id[x]+siz[x]-1));
ans+=query(id[x],id[x]+siz[x]-1);
ans+=ask_sum(1,id[x]+1,id[x]+siz[x]-1);
printf("%u\n",ans);
}
if(op==4){
w=read();
change_val(1,id[x],id[x]+siz[x]-1,w);
}
if(op==5||op==6){
rev(x);
}
}
return 0;
}

noip模拟46的更多相关文章

  1. Noip模拟46 2021.8.23

    给了签到题,但除了签到题其他的什么也不会.... T1 数数 人均$AC$,没什么好说的,就是排个序,然后双指针交换着往中间移 1 #include<bits/stdc++.h> 2 #d ...

  2. [NOIP模拟46]鼠树

    神仙题. 首先不考虑把黑点变白,发现每个白点的信息与它的归属点是相同的.可以在线段树中只维护黑点的信息,再记录$DFS$序上每个点之前黑点个数的前缀和,每次操作可以二分出该点的归属点进行操作. 具体维 ...

  3. 8.23考试总结(NOIP模拟46)[数数·数树·鼠树·ckw的树]

    T1 数数 解题思路 大概是一个签到题的感觉...(但是 pyt 并没有签上) 第一题当然可以找规律,但是咱们还是老老实实搞正解吧... 先从小到大拍个序,这样可以保证 \(a_l<a_r\) ...

  4. [考试总结]noip模拟46

    脑袋确实是不好使了需要回家暴颓治疗 数数数树鼠树 真好玩. 数数 大水题一个,妥妥的签到题目,然后... 我没签上 气展了!!! 其实我还是想麻烦了. 就是我们实际上就是排序之后每一次找头上和尾巴上的 ...

  5. NOIP模拟17.9.22

    NOIP模拟17.9.22 前进![问题描述]数轴的原点上有一只青蛙.青蛙要跳到数轴上≥

  6. NOIP 模拟4 T2

    本题属于二和一问题 子问题相互对称 考虑对于问题一:知a求b 那么根据b数组定义式 显然能发现问题在于如何求dis(最短路) 有很多算法可供选择 dijsktra,floyed,bfs/dfs,spf ...

  7. 2021.9.17考试总结[NOIP模拟55]

    有的考试表面上自称NOIP模拟,背地里却是绍兴一中NOI模拟 吓得我直接文件打错 T1 Skip 设状态$f_i$为最后一次选$i$在$i$时的最优解.有$f_i=max_{j<i}[f_j+a ...

  8. NOIP模拟赛20161022

    NOIP模拟赛2016-10-22 题目名 东风谷早苗 西行寺幽幽子 琪露诺 上白泽慧音 源文件 robot.cpp/c/pas spring.cpp/c/pas iceroad.cpp/c/pas ...

  9. contesthunter暑假NOIP模拟赛第一场题解

    contesthunter暑假NOIP模拟赛#1题解: 第一题:杯具大派送 水题.枚举A,B的公约数即可. #include <algorithm> #include <cmath& ...

随机推荐

  1. Hadoop 3.1.1 - Yarn - 使用 GPU

    在 Yarn 上使用 GPU 前提 目前,Yarn 只支持 Nvidia GPU. YARN NodeManager 所在机器必须预先安装了 Nvidia 驱动器. 如果使用 Docker 作为容器的 ...

  2. QT 如何在调试时能进入源码方式(MacOS)

    最近在学习QT, 遇到一些crash, 也没看过QT源码啊, 就想类似Java一样, 在出错时进入源码跟踪一下, 但是QT和Java太不一样了, 死活进不去. 研究了几天, 发现本来是很简单的事情, ...

  3. DC-9 靶机渗透测试

    DC-9 渗透测试 冲冲冲,好好学习 DC系列的9个靶机做完了,对渗透流程基本掌握,但是实战中出现的情况千千万万,需要用到的知识面太广了,学不可以已. 靶机IP: 172.66.66.139 kali ...

  4. 字节跳动、快手等大厂Android面试刨根问底之内存泄露篇

    现在快手字节跳动等公司都在大量招人,薪资优厚,但是想进去却没那么简单,面过的人都知道,这些公司的面试官巴不得把你会的东西都给你挖出来,所以要深入复习知识点,让自己耐问一点.一下是针对内存泄露真实面试过 ...

  5. 树莓派压力测试工具S-Tui + Stress的使用

    压力测试工具S-Tui + Stress的使用 树莓派# 背景故事 打算测试一下树莓派外壳的散热性能,需要能压满CPU100%,同时显示温度.CPU频率.CPU占用率,那么这款工具你值得拥有. 软件介 ...

  6. Python3中dict字典的相关操作函数

    字典对象的内建函数 1. clear() 清空字典. 例: >>> a = {1:3, 2:4} >>> a.clear() >>> a {} 2 ...

  7. idea构建servlet程序

    1 新建maven项目 勾选maven_web模板 2 idea加载后应是如图所示 3 在main目录下新增两个文件夹,一个java 设置为源码根目录,另一个是resources 设置为源目录 4 在 ...

  8. Semi-automation Script Based on Sleep

    The following script auto login to server 49, send 2 commands and exit from the server. Create a aut ...

  9. Linux 学习Shell一部分指令

    接下来就是shell命令的一些演示了 set (超级多的变量和系统默认值) echo $?查看上一条指令是否执行成功 返回0意味着成功,返回1意味着失败 echo 是个 env 设置变量 解释一下上面 ...

  10. go配置私有仓库 (go mod配置私有仓库)

    windows 配置go私有仓库 一.环境 1.私有gitlab (gitlab.xxx.com) 2.go 1.16.3 3.win10系统, 家目录:C:\Users\Administrator, ...