noip模拟46
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的更多相关文章
- Noip模拟46 2021.8.23
给了签到题,但除了签到题其他的什么也不会.... T1 数数 人均$AC$,没什么好说的,就是排个序,然后双指针交换着往中间移 1 #include<bits/stdc++.h> 2 #d ...
- [NOIP模拟46]鼠树
神仙题. 首先不考虑把黑点变白,发现每个白点的信息与它的归属点是相同的.可以在线段树中只维护黑点的信息,再记录$DFS$序上每个点之前黑点个数的前缀和,每次操作可以二分出该点的归属点进行操作. 具体维 ...
- 8.23考试总结(NOIP模拟46)[数数·数树·鼠树·ckw的树]
T1 数数 解题思路 大概是一个签到题的感觉...(但是 pyt 并没有签上) 第一题当然可以找规律,但是咱们还是老老实实搞正解吧... 先从小到大拍个序,这样可以保证 \(a_l<a_r\) ...
- [考试总结]noip模拟46
脑袋确实是不好使了需要回家暴颓治疗 数数数树鼠树 真好玩. 数数 大水题一个,妥妥的签到题目,然后... 我没签上 气展了!!! 其实我还是想麻烦了. 就是我们实际上就是排序之后每一次找头上和尾巴上的 ...
- NOIP模拟17.9.22
NOIP模拟17.9.22 前进![问题描述]数轴的原点上有一只青蛙.青蛙要跳到数轴上≥
- NOIP 模拟4 T2
本题属于二和一问题 子问题相互对称 考虑对于问题一:知a求b 那么根据b数组定义式 显然能发现问题在于如何求dis(最短路) 有很多算法可供选择 dijsktra,floyed,bfs/dfs,spf ...
- 2021.9.17考试总结[NOIP模拟55]
有的考试表面上自称NOIP模拟,背地里却是绍兴一中NOI模拟 吓得我直接文件打错 T1 Skip 设状态$f_i$为最后一次选$i$在$i$时的最优解.有$f_i=max_{j<i}[f_j+a ...
- NOIP模拟赛20161022
NOIP模拟赛2016-10-22 题目名 东风谷早苗 西行寺幽幽子 琪露诺 上白泽慧音 源文件 robot.cpp/c/pas spring.cpp/c/pas iceroad.cpp/c/pas ...
- contesthunter暑假NOIP模拟赛第一场题解
contesthunter暑假NOIP模拟赛#1题解: 第一题:杯具大派送 水题.枚举A,B的公约数即可. #include <algorithm> #include <cmath& ...
随机推荐
- 使用C#winform编写渗透测试工具--Web指纹识别
使用C#winform编写渗透测试工具--web指纹识别 本篇文章主要介绍使用C#winform编写渗透测试工具--Web指纹识别.在渗透测试中,web指纹识别是信息收集关键的一步,通常是使用各种工具 ...
- 自学linux——2.认识目录及常用指(命)令
认识目录及常用指(命)令 1.备份: 快照(还原精灵):短期备份 频繁备份 可关可开.可能会影响系统的操作. 备份时:虚拟机--快照 还原时:虚拟机--快照--快照管理器--相应位置--转到 克隆 ...
- 双非本科字节跳动Android面试题分享(已拿offer)
基本情况 本人系非985非211普通本科生一枚,有实习有项目经历但成绩普通,在面试前刷了很多面经.面试题,这里也把自己的分享下,做个回报好了,顺便攒攒人品,一到这种时候人就迷信起来了. 面试是以视频面 ...
- Map 与 unordered_map 横向与纵向测试,附带原始数据与测试程序
写程序时,面临用Map还是unordered_map,总是很纠结,于是写了个程序进行测试 Map 与 unordered_map 横向与纵向测试,附带原始数据与测试程序 简单数据(4 Byte) 首先 ...
- HDFS总结
hadoop分布式文件存储系统,用来解决海量数据的存储问题 HDFS的组成------核心配置文件:hdfs-site.xml.core-site.xml NameNode:负责整个HDFS集群的管理 ...
- pom.xml中web.xml is missing and <failOnMissingWebXml> is set to true错误的解决
.personSunflowerP { background: rgba(51, 153, 0, 0.66); border-bottom: 1px solid rgba(0, 102, 0, 1); ...
- Java-SpringBoot注解方式实现AOP
AOP基本总结 连接点(JoinPoint): 连接点是程序运行的某个阶段点,如方法调用.异常抛出等 切入点(Pointcut): 切入点是JoinPoint的集合 是程序中需要注入Advice的位置 ...
- 001 七层OSI参考模型
一.什么是七层OSI参考模型 OSI(Open System Interconnect),即开放式系统互连.是国际标准化组织(ISO)制定的一个用于计算机或通信系统间互联的标准体系,一般称为OSI参考 ...
- SwiftUI图片处理(缩放、拼图)
采用SwiftUI Core Graphics技术,与C#的GDI+绘图类似,具体概念不多说,毕竟我也是新手,本文主要展示效果图及代码,本文示例代码需要请拉到文末自取. 1.图片缩放 完全填充,变形压 ...
- CTF-flask模板注入学习
今天又看到了一道这样的题,之前一直都学不明白的东西 反反复复给你看的时候,就想搞明白了. 我们做题的,需要知道flask是怎么运行的就行了. 这个就是一个最简单的flask应用,当我们访问的时候,就会 ...