8.23考试总结(NOIP模拟46)[数数·数树·鼠树·ckw的树]
T1 数数
解题思路
大概是一个签到题的感觉。。。(但是 pyt 并没有签上)
第一题当然可以找规律,但是咱们还是老老实实搞正解吧。。。
先从小到大拍个序,这样可以保证 \(a_l<a_r\) 直接去掉绝对值。
然后就可以推出如下柿子:
\]
\]
然后就会发现 \(a_i\) 的贡献的正负与下标有关系那么对于 \(i<\dfrac{k+1}{2}\) 尽量选择小的数字。
同样的,对于 \(i>\dfrac{k+1}{2}\) 尽量选择比较大的数字。
对于不同的 \(k\) 每一个 \(a_i\) 的系数都是会发生变化的,简单处理一下就好了。
code
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=3e5+10;
int n,l,r,sum1,sum2,pre[N],s[N];
signed main()
{
n=read();
for(int i=1;i<=n;i++)
s[i]=read();
l=0; r=n+1;
sort(s+1,s+n+1);
for(int i=1;i<=n;i++)
pre[i]=pre[i-1]+s[i];
for(int k=1;k<=n;k++)
{
int pos=(k+1)/2;
sum1-=pre[l]; sum2+=pre[n]-pre[r-1];
while(l<pos) l++,sum1+=s[l]*(2*l-k-1);
while(n-r+1<pos) r--,sum2+=s[r]*(2*(-n+r)+k-1);
printf("%lld\n",sum2+sum1);
}
return 0;
}
T2 数树
解题思路
树上背包 DP 。
\(f_{i,j,0/1/2/3}\) 表示 节点 \(i\) 目前至少有 \(j\) 条不合法的边,并且当前节点状态是:没有出入边,有一条出边,有一条入边,有一条出边一条入边。
然后进行树形 DP 子树合并计算出叶子节点对于自身的贡献,当然也要把没有贡献的继承过来。
最后算出来的结果运用一个类似于二项式反演的东西就可以得到恰好没有不合法边的状态。
为什么说是类似呢,它的系数是有一些差别的 \((n-i)!\) ,毕竟对于有 \(i\) 条不合法的边而言,它所链接的两个点其实可以看作是一个点,然后就是一个数值的排问题了。
code
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=5e3+10,mod=998244353;
int n,ans,fac[N],siz[N],f[N][N][4],g[N][4];
int tot,head[N],nxt[N<<1],edge[N<<1],ver[N<<1];
void add_edge(int x,int y,int val)
{
ver[++tot]=y;
nxt[tot]=head[x];
edge[tot]=val;
head[x]=tot;
}
void dfs(int x)
{
siz[x]=1; f[x][0][0]=1;
for(int i=head[x];i;i=nxt[i])
{
int to=ver[i];
if(siz[to]) continue;
dfs(to);
memset(g,0,sizeof(g));
for(int j=0;j<siz[x];j++)
for(int k=0;k<siz[to];k++)
{
int cnt=(f[to][k][0]+f[to][k][1]+f[to][k][2]+f[to][k][3])%mod;
switch(edge[i])
{
case 1:
(g[j+k+1][1]+=f[x][j][0]*(f[to][k][0]+f[to][k][1]))%=mod;
(g[j+k+1][3]+=f[x][j][2]*(f[to][k][0]+f[to][k][1]))%=mod;
break;
case 0:
(g[j+k+1][2]+=f[x][j][0]*(f[to][k][0]+f[to][k][2]))%=mod;
(g[j+k+1][3]+=f[x][j][1]*(f[to][k][0]+f[to][k][2]))%=mod;
break;
}
(g[j+k][0]+=cnt*f[x][j][0])%=mod;
(g[j+k][1]+=cnt*f[x][j][1])%=mod;
(g[j+k][2]+=cnt*f[x][j][2])%=mod;
(g[j+k][3]+=cnt*f[x][j][3])%=mod;
}
siz[x]+=siz[to];
for(int j=0;j<siz[x];j++)
{
f[x][j][0]=g[j][0];
f[x][j][1]=g[j][1];
f[x][j][2]=g[j][2];
f[x][j][3]=g[j][3];
}
}
}
signed main()
{
n=read();
for(int i=1,x,y;i<n;i++)
{
x=read(); y=read();
add_edge(x,y,1); add_edge(y,x,0);
}
dfs(1); fac[0]=1;
for(int i=1;i<=n;i++)
fac[i]=fac[i-1]*i%mod;
for(int i=0,temp;i<n;i++)
{
temp=0;
(temp+=f[1][i][0])%=mod;
(temp+=f[1][i][1])%=mod;
(temp+=f[1][i][2])%=mod;
(temp+=f[1][i][3])%=mod;
if(i&1) (ans+=mod-fac[n-i]*temp%mod)%=mod;
else (ans+=fac[n-i]*temp%mod)%=mod;
}
printf("%lld",ans);
return 0;
}
T3 鼠树
解题思路
其实题写的挺艰辛的。。
考场上是想到了一个两颗线段树维护(一棵维护归属点,一棵维护权值)的办法,但是貌似单次修改的复杂度是 \(nlog^2n\) 的,还不如暴力。。
最主要的是它的 3 操作还是有问题的。。(\(code\))
正解的做法就比较神仙了。
开一个 set
维护单个点的归属点,具体实现主要结合树链剖分,维护每一条链最顶端的点 set
并且记录该 set
里的深度最小值,与目前的点的深度相比较。
时间复杂度大概是 \(log\;n\) ,这里的 set
是按照深度由小到大排的序。
还有两棵线段树,一个维护黑色节点在它的所有管辖点应该下放的权值还有范围以及权值和,另一个维护因为节点操作的缘故,而出现的比较杂碎的权值。
第二个的话,其实就是区间修改,区间查询的线段树,也可以用树状数组来实现。
为了方便下文将会把第一颗线段树称为 T1 ,第二颗称为 T2 。
实现的话分操作讲一下吧。。
1 操作除了需要查询归属点应该下放的权值外也要计算一下 T2 里面琐碎的权值。
2 操作直接在 T1 里单点修改权值就好了。
3 操作查询 T1 子树范围内的管辖点应下放的权值与范围的乘积,也要算上计算 T2 子树范围内的权值和,还有就是对于子树根节点向下一部分的点,他们的管辖点可能是在子树之外的,这里也需要算进去。
4 操作直接给 T1 区间修改就好了。
5 操作就开始有一点恶心人了,先是查找更改之前当前节点(计为 \(x\) )的归属点(计为 \(att\)),将现在应该加到 \(x\) 上的范围减去,然后将范围加入到 \(x\) 上,对于之前的下放权值继承到 \(x\) 上。
6操作与 5 操作有一些类似,同样是范围的加减继承,同样的把相对于 \(att\) 多的权值区间修改到 T2 上,这也就是维护两颗线段树的意义所在。
code
#include<bits/stdc++.h>
#define ui unsigned int
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
#define ls x<<1
#define rs x<<1|1
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=1e6+10,INF=1e9;
int n,m,minn[N];
int tot,head[N],nxt[N<<1],ver[N<<1];
int tim,dfn[N],siz[N],son[N],dep[N],fa[N],topp[N];
struct Sort{bool operator ()(int x,int y){return dep[x]<dep[y];}};
set<int,Sort> s[N];
struct Segment_Tree1
{
struct Node
{
ui val,ran,dat;
}tre[N<<2];
void push_down(int x)
{
if(!tre[x].val) return ;
if(tre[ls].ran)
{
tre[ls].val+=tre[x].val;
tre[ls].dat+=tre[x].val*tre[ls].ran;
}
if(tre[rs].ran)
{
tre[rs].val+=tre[x].val;
tre[rs].dat+=tre[x].val*1tre[rs].ran;
}
tre[x].val=0;
}
void push_up(int x)
{
tre[x].ran=tre[ls].ran+tre[rs].ran;
tre[x].dat=tre[ls].dat+tre[rs].dat;
}
ui query_val(int x,int l,int r,int pos)
{
if(l==r) return tre[x].val;
push_down(x);
int mid=(l+r)>>1; ui sum=0;
if(pos<=mid) sum=query_val(ls,l,mid,pos);
else sum=query_val(rs,mid+1,r,pos);
push_up(x);
return sum;
}
ui query_dat(int x,int l,int r,int L,int R)
{
if(L<=l&&r<=R) return tre[x].dat;
push_down(x);
int mid=(l+r)>>1; ui sum=0;
if(L<=mid) sum+=query_dat(ls,l,mid,L,R);
if(R>mid) sum+=query_dat(rs,mid+1,r,L,R);
push_up(x);
return sum;
}
int query_ran(int x,int l,int r,int L,int R)
{
if(L<=l&&r<=R) return tre[x].ran;
push_down(x);
int mid=(l+r)>>1,sum=0;
if(L<=mid) sum+=query_ran(ls,l,mid,L,R);
if(R>mid) sum+=query_ran(rs,mid+1,r,L,R);
push_up(x);
return sum;
}
void insert_val(int x,int l,int r,int L,int R,ui num)
{
if(L<=l&&r<=R)
{
if(!tre[x].ran) return;
tre[x].val+=num;
tre[x].dat+=num*tre[x].ran;
return ;
}
push_down(x);
int mid=(l+r)>>1;
if(L<=mid) insert_val(ls,l,mid,L,R,num);
if(R>mid) insert_val(rs,mid+1,r,L,R,num);
push_up(x);
}
void insert_ran(int x,int l,int r,int pos,ui num)
{
if(l==r)
{
tre[x].ran+=num;
if(!tre[x].ran) tre[x].val=0;
tre[x].dat=tre[x].val*tre[x].ran;
return ;
}
push_down(x);
int mid=(l+r)>>1;
if(pos<=mid) insert_ran(ls,l,mid,pos,num);
else insert_ran(rs,mid+1,r,pos,num);
push_up(x);
}
}t1;
struct Segment_Tree2
{
struct Node
{
ui dat,laz;
}tre[N<<1];
void push_down(int x,int l,int r)
{
if(!tre[x].laz) return ;
int mid=(l+r)>>1;
tre[ls].laz+=tre[x].laz; tre[rs].laz+=tre[x].laz;
tre[ls].dat+=(mid-l+1)*tre[x].laz;
tre[rs].dat+=(r-mid)*tre[x].laz;
tre[x].laz=0;
}
void push_up(int x)
{
tre[x].dat=tre[ls].dat+tre[rs].dat;
}
void insert(int x,int l,int r,int L,int R,ui num)
{
if(L<=l&&r<=R)
{
tre[x].dat+=num*(r-l+1);
tre[x].laz+=num;
return ;
}
push_down(x,l,r);
int mid=(l+r)>>1;
if(L<=mid) insert(ls,l,mid,L,R,num);
if(R>mid) insert(rs,mid+1,r,L,R,num);
push_up(x);
}
ui query(int x,int l,int r,int L,int R)
{
if(L<=l&&r<=R) return tre[x].dat;
push_down(x,l,r);
int mid=(l+r)>>1; ui sum=0;
if(L<=mid) sum+=query(ls,l,mid,L,R);
if(R>mid) sum+=query(rs,mid+1,r,L,R);
push_up(x);
return sum;
}
}t2;
void add_edge(int x,int y)
{
ver[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
}
void dfs1(int x)
{
siz[x]=1;
dfn[x]=++tim;
for(int i=head[x];i;i=nxt[i])
{
int to=ver[i];
fa[to]=x; dep[to]=dep[x]+1;
dfs1(to);
siz[x]+=siz[to];
if(siz[to]>siz[son[x]])
son[x]=to;
}
}
void dfs2(int x,int tp)
{
topp[x]=tp;
if(son[x]) dfs2(son[x],tp);
for(int i=head[x];i;i=nxt[i])
if(ver[i]!=son[x])
dfs2(ver[i],ver[i]);
}
int find_att(int x)
{
while(x)
{
if(minn[topp[x]]<=dep[x])
{
auto pos=s[topp[x]].upper_bound(x); pos--;
return (*pos);
}
x=fa[topp[x]];
}
}
void update_min(int x)
{
minn[x]=(s[x].size()?dep[*(s[x].begin())]:INF);
}
signed main()
{
n=read(); m=read();
for(int i=2,x;i<=n;i++)
x=read(),add_edge(x,i);
dfs1(1); dfs2(1,1);
memset(minn,0x7f,sizeof(minn));
s[1].insert(1); update_min(1);
t1.insert_ran(1,1,n,dfn[1],siz[1]);
while(m--)
{
int opt,x,val,att;
opt=read(); x=read();
if(opt==2||opt==4) val=read();
if(opt==1||opt==3||opt==5) att=find_att(x);
if(opt==6) att=find_att(fa[x]);
if(opt==1) printf("%u\n",t1.query_val(1,1,n,dfn[att])+t2.query(1,1,n,dfn[x],dfn[x]));
if(opt==2) t1.insert_val(1,1,n,dfn[x],dfn[x],val);
if(opt==3)
{
int att=find_att(x);
ui ran=siz[x]-t1.query_ran(1,1,n,dfn[x],dfn[x]+siz[x]-1);
ui sum=t2.query(1,1,n,dfn[x],dfn[x]+siz[x]-1);
sum+=t1.query_dat(1,1,n,dfn[x],dfn[x]+siz[x]-1);
sum+=t1.query_val(1,1,n,dfn[att])*ran;
printf("%u\n",sum);
}
if(opt==4) t1.insert_val(1,1,n,dfn[x],dfn[x]+siz[x]-1,val);
if(opt==5)
{
int att=find_att(x);
ui ran=siz[x]-t1.query_ran(1,1,n,dfn[x],dfn[x]+siz[x]-1);
ui val=t1.query_val(1,1,n,dfn[att]);
t1.insert_ran(1,1,n,dfn[att],-ran);
t1.insert_ran(1,1,n,dfn[x],ran);
t1.insert_val(1,1,n,dfn[x],dfn[x],val);
s[topp[x]].insert(x); update_min(topp[x]);
}
if(opt==6)
{
int att=find_att(fa[x]);
ui ran=t1.query_ran(1,1,n,dfn[x],dfn[x]);
ui val=t1.query_val(1,1,n,dfn[x]);
t1.insert_ran(1,1,n,dfn[att],ran);
t1.insert_ran(1,1,n,dfn[x],-ran);
val-=t1.query_val(1,1,n,dfn[att]);
t2.insert(1,1,n,dfn[x],dfn[x]+siz[x]-1,val);
t1.insert_val(1,1,n,dfn[x],dfn[x]+siz[x]-1,-val);
s[topp[x]].erase(x); update_min(topp[x]);
}
}
return 0;
}
8.23考试总结(NOIP模拟46)[数数·数树·鼠树·ckw的树]的更多相关文章
- 5.23考试总结(NOIP模拟2)
5.23考试总结(NOIP模拟2) 洛谷题单 看第一题第一眼,不好打呀;看第一题样例又一眼,诶,我直接一手小阶乘走人 然后就急忙去干T2T3了 后来考完一看,只有\(T1\)骗到了\(15pts\)[ ...
- [考试总结]noip模拟46
脑袋确实是不好使了需要回家暴颓治疗 数数数树鼠树 真好玩. 数数 大水题一个,妥妥的签到题目,然后... 我没签上 气展了!!! 其实我还是想麻烦了. 就是我们实际上就是排序之后每一次找头上和尾巴上的 ...
- 6.17考试总结(NOIP模拟8)[星际旅行·砍树·超级树·求和]
6.17考试总结(NOIP模拟8) 背景 考得不咋样,有一个非常遗憾的地方:最后一题少取膜了,\(100pts->40pts\),改了这么多年的错还是头一回看见以下的情景... T1星际旅行 前 ...
- 5.22考试总结(NOIP模拟1)
5.22考试总结(NOIP模拟1) 改题记录 T1 序列 题解 暴力思路很好想,分数也很好想\(QAQ\) (反正我只拿了5pts) 正解的话: 先用欧拉筛把1-n的素数筛出来 void get_Pr ...
- 2019.8.3 [HZOI]NOIP模拟测试12 B. 数颜色
2019.8.3 [HZOI]NOIP模拟测试12 B. 数颜色 全场比赛题解:https://pan.baidu.com/s/1eSAMuXk 数据结构学傻的做法: 对每种颜色开动态开点线段树直接维 ...
- [考试总结]noip模拟23
因为考试过多,所以学校的博客就暂时咕掉了,放到家里来写 不过话说,vscode的markdown编辑器还是真的很好用 先把 \(noip\) 模拟 \(23\) 的总结写了吧.. 俗话说:" ...
- Noip模拟46 2021.8.23
给了签到题,但除了签到题其他的什么也不会.... T1 数数 人均$AC$,没什么好说的,就是排个序,然后双指针交换着往中间移 1 #include<bits/stdc++.h> 2 #d ...
- 2021.9.17考试总结[NOIP模拟55]
有的考试表面上自称NOIP模拟,背地里却是绍兴一中NOI模拟 吓得我直接文件打错 T1 Skip 设状态$f_i$为最后一次选$i$在$i$时的最优解.有$f_i=max_{j<i}[f_j+a ...
- 2021.7.21考试总结[NOIP模拟22]
终于碾压小熠了乐死了 T1 d 小贪心一波直接出正解,没啥好说的(bushi 好像可以主席树暴力找,但我怎么可能会呢?好像可以堆优化简单找,但我怎么可能想得到呢? 那怎么办?昨天两道单调指针加桶,我直 ...
随机推荐
- js定时器中引用的外部函数如何传递参数
问题:比如在一个点击事件中我需要将点击事件参数event传入到定时器中,如果只是单纯的在setTimeout(timer(e),1000)中写上括弧e,则该定时器不会等到1s才执行,而是会立即执行.那 ...
- Java基础——自己的总结
Java Java是什么? Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承.指针等概念,因此Java语言具有功能强大和简单易用两个特征.Java语言作 ...
- Git submodule 拉取子模块
$ git clone https://code.Xcode.com.client.git Cloning into 'vipkid-pc-client'... Username for 'https ...
- C语言:位运算符总结
位运算符:1.指对操作数以二进制位( bit)为单位进行的数据处理2.每一个二进制位只存放0或13. 取反:~ 按位反 ~ 0变1 1变0 ~1=0 ~0=14.异或: ^ 相同为0,不相同为1 1 ...
- 在deeping上安装mariadb
1,安装的官网参考:有安装的命令和指导https://downloads.mariadb.org/mariadb/repositories/#distro=Debian&distro_rele ...
- 数组去重汇总—v客学院技术分享
上周基础班结束了数组的学习内容,这几天有时间整理了下几种比较常用的数组去重的方法供大家查阅!!!!! 话不多说,直接贴代码吧~~~~~~~ 欢迎大家指正,共同学习,一同进步!!! (php开发,web ...
- python里面的==,is的区别
python中对象的三个要素:id(身份标示),type(数据类型).value(值) == 比较操作符:用来比较两个对象值是否相等. is 同一性运算符:比较两个对象的id值是否相等,即是否是同 ...
- python解析excel
import xlrd, base64excel_obj = xlrd.open_workbook(file_contents=base64.decodestring(filename)).#打开要解 ...
- VM安装kali操作系统
工具:VMware Workstation 15 Pro(15.5.6 build-16341506),kali-linux-2020.2-installer-amd64.iso vm15下载链接:h ...
- 第十三篇 -- QMainWindow与QAction(新建-打开-保存)
效果图: 添加了三个Action,分别是新建,打开,和保存,没有具体写相应的功能,只是提供了一个接口,可以自己写相应的功能.这次不仅将这些Action放在了工具栏,还将其添加到了菜单栏.方法同样是直接 ...