\(\color{white}{\mathbb{百般红紫博众爱,正是芳菲斗艳时,名之以:牡丹}}\)


%%% szs巨佬AK

\(t1\)、\(t4\) 都会做,剩下两道好像都不太会,再次扫描到知识盲区——竞赛图

特殊的 \(dp\) 题还是很难设计出一个标准的状态


(t1过水已隐藏)

B. 竞赛图

数据范围小一看是状压,然而并不知道竞赛图的性质,于是瞎推一阵后只打了个 \(tarjan\) 暴力,一测 \(19\) 的点跑了 \(2.3s\),以为能蹭过 \(60\) 的点(然而最后并没有)

竞赛图缩点后是链状,那么对于一个非强联通图,一定存在且仅存在一个子图 \(S\) 向补集的连边都是朝向补集方向的。那么对于每个集合 \(S\),对其所有 所有点的出边交集的子集标记为不合法即可

代码实现
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e7+5;
int n,all,op,ans,t,to[maxn];
bool flag[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;
}
void init(){
for(int i=0;i<=all;i++)flag[i]=1,to[i]=0;
ans=0;
return ;
}
int main(){
t=read();
for(int i=1;i<=2e7;i++)flag[i]=1;
while(t--){
init();
n=read();
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
op=read();
if(op)to[1<<(i-1)]|=(1<<(j-1));
}
}
all=(1<<n)-1;
to[0]=all;
for(int S=1;S<=all;S++){
int p=(S&-S);
to[S]=(to[p]&to[S^p]);
// cout<<S<<" "<<to[S]<<endl;
}
for(int S=1;S<=all;S++){
if(flag[S]){
for(int T=to[S];T;T=(T-1)&to[S]){
flag[T|S]=false;
}
}
}
for(int S=0;S<=all;S++)if(flag[S])ans++;
cout<<ans<<endl;
}
return 0;
}

C. 糖果

\(dp\) 神仙题

首先可以只关心 \(C\) 组选了哪些数,对于所选数的位置以及未选数的排列可以计算

考虑对于一个确定的数组排列,第一次一定是放第一个数,那么第一次前两个数的放置有 \((n-1)(n-2)\) 种,第二次摆放后有 \((n-4)(n-5)\) 种方法,那么最后答案只需要乘上 \(\prod (3i-1)(3i-2)\) 即可。

考虑 \(dp\) 统计,设 \(dp[i][j][k]\) 表示第一行扫到第 \(i\) 个位置,第二行扫到第 \(j\) 个位置,第三行有 \(k\) 个数在候选集合里的方案数。最后加一为维 \(0/1\) 表示当前更新是第几行,然后 \(dp\) 一列一列转移,先选第一行,再选第二行,用 \(0\) 转移到 \(1\),用 \(1\) 转移到下一个 \(0\)

考虑转移:

对于第一行的一个数可以选当且仅当第一行当前位置的数在第二行出现的位置晚于 \(j\),否则只能光移不选,于是有一下转移:

光移不选:

\[f[i+1][j][k][0]+=f[i][j][k][0]
\]

选择当前,更新第二行:

\[f[i][j][k][1]+=f[i][j][k][0]
\]

并更新第三行的备选集合(因为用了一个候选集合就少了一个):

\[f[i+1][j][k-1][0]+=f[i][j][k][0]
\]

第二行类似,更新备选集合为:

\[f[i+1][j][k+1][0]+=f[i][j][k][1]
\]

因为右移了一位,相当于候选集合里多了一个数

于是整个 \(dp\) 复杂度是 \(n^3\) 的

代码实现
#include<bits/stdc++.h>
using namespace std;
const int maxn=405;
const int mod=1e9+7;
int n,a[maxn],posa[maxn],b[maxn],posb[maxn],f[maxn][maxn][maxn][2],ans;
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;
}
int main(){
n=read();
for(int i=1;i<=n;i++)a[i]=read(),posa[a[i]]=i;
for(int i=1;i<=n;i++)b[i]=read(),posb[b[i]]=i;
f[1][1][0][0]=1;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
for(int k=0;k<=n/3;k++){
if(f[i][j][k][0]){
if(posb[a[i]]<j){
f[i+1][j][k][0]=(f[i+1][j][k][0]+f[i][j][k][0])%mod;
}
else {
f[i][j][k][1]=(f[i][j][k][1]+f[i][j][k][0])%mod;
if(k)f[i+1][j][k-1][0]=(f[i+1][j][k-1][0]+1ll*f[i][j][k][0]*k%mod)%mod;
}
}
if(f[i][j][k][1]){
if(posa[b[j]]<i){
f[i][j+1][k][1]=(f[i][j+1][k][1]+f[i][j][k][1])%mod;
}
else if(b[j]!=a[i]){
if(k)f[i][j+1][k-1][1]=(f[i][j+1][k-1][1]+1ll*f[i][j][k][1]*k%mod)%mod;
f[i+1][j+1][k+1][0]=(f[i+1][j+1][k+1][0]+f[i][j][k][1])%mod;
}
}
}
} }
for(int i=1;i<=n;i++)ans=(ans+f[n+1][i][0][0])%mod;
for(int i=1;i<=n/3;i++)ans=1ll*ans*(3*i-1)%mod*(3*i-2)%mod;
cout<<ans;
return 0;
}

D. 树

国赛 \(d1t1\) 原题,一条黑边相当于两端点更新时间不同,那么每次修改把路径上的节点时间戳都更新即可

代码实现
#include<bits/stdc++.h>
using namespace std;
const int maxn=6e5+5;
const int maxm=1e6+5;
int n,q,x,y,hd[maxn],cnt,siz[maxn],fa[maxn],tp[maxn],son[maxn],op,ans,re[maxn],tot,id[maxn],dep[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(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;
fa[v]=u;
dep[v]=dep[u]+1;
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==fa[u]||v==son[u])continue;
dfs1(v,v);
}
return ;
}
namespace p1{
int tim,dfn[maxn],d;
int lca(int x,int y){
while(tp[x]!=tp[y]){
if(dep[tp[x]]<dep[tp[y]])swap(x,y);
x=fa[tp[x]];
}
if(dep[x]>dep[y])swap(x,y);
return x;
}
void start(){
for(int i=1;i<=n;i++)dfn[i]=i;
tim=n;
for(int i=1;i<=q;i++){
op=read();
x=read();
y=read();
d=lca(x,y);
if(op==1){
tim++;
while(1){
// cout<<x<<" ";
dfn[x]=tim;
if(x==d)break;
x=fa[x];
}
while(y!=d){
dfn[y]=tim;
y=fa[y];
}
}
else{
ans=0;
while(x!=d){
ans+=(dfn[x]!=dfn[fa[x]]);
x=fa[x];
}
while(y!=d){
ans+=(dfn[y]!=dfn[fa[y]]);
y=fa[y];
}
printf("%d\n",ans);
}
}
return ;
}
}
namespace p2{
int dfn[maxn],tim;
struct Seg{
int l,r,lc,rc,num,lazy;
}t[maxn*4];
void merge(Seg &p,Seg x,Seg y){
if(!x.num){
p=y;
return ;
}
if(!y.num){
p=x;
return ;
}
p.num=x.num+y.num-(x.rc==y.lc);
p.lc=x.lc;
p.rc=y.rc;
return ;
}
void build(int p,int l,int r){
t[p].l=l;
t[p].r=r;
if(l==r){
t[p].num=1;
t[p].lc=t[p].rc=dfn[re[l]];
return ;
}
int mid=t[p].l+t[p].r>>1;
build(p<<1,l,mid);
build(p<<1|1,mid+1,r);
merge(t[p],t[p<<1],t[p<<1|1]);
return ;
}
void dospread(int p,int w){
t[p].num=1;
t[p].lc=t[p].rc=w;
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(int p,int l,int r,int w){
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(p<<1,l,r,w);
if(r>mid)change(p<<1|1,l,r,w);
merge(t[p],t[p<<1],t[p<<1|1]);
return ;
}
void modi(int x,int y){
while(tp[x]!=tp[y]){
if(dep[tp[x]]<dep[tp[y]])swap(x,y);
change(1,id[tp[x]],id[x],tim);
x=fa[tp[x]];
}
if(dep[x]<dep[y])swap(x,y);
change(1,id[y],id[x],tim);
// cout<<"ppp "<<x<<" "<<y<<" "<<id[y]<<" "<<id[x]<<" "<<t[1].num<<endl;
return ;
}
Seg ask(int p,int l,int r){
// cout<<l<<" "<<r<<" "<<t[p].l<<" "<<t[p].r<<endl;
if(t[p].l>=l&&t[p].r<=r)return t[p];
if(t[p].lazy)spread(p);
int mid=t[p].l+t[p].r>>1;
Seg ans;
ans.num=ans.lc=ans.rc=0;
if(l<=mid)ans=ask(p<<1,l,r);
if(r>mid)merge(ans,ans,ask(p<<1|1,l,r));
return ans;
}
int que(int x,int y){
Seg ansx,ansy;
ansx.num=ansx.lc=ansx.rc=ansy.num=ansy.lc=ansy.rc=0;
while(tp[x]!=tp[y]){
if(dep[tp[x]]<dep[tp[y]])swap(x,y),swap(ansx,ansy);
merge(ansx,ask(1,id[tp[x]],id[x]),ansx);
// cout<<x<<" "<<tp[tp[x]]<<" "<<id[x]<<" "<<id[x]<<" "<<ansx.num<<endl;
x=fa[tp[x]];
}
if(dep[x]<dep[y])swap(x,y),swap(ansx,ansy);
merge(ansx,ask(1,id[y],id[x]),ansx);
// cout<<x<<" "<<y<<" "<<id[y]<<" "<<id[x]<<" "<<ansx.num<<" "<<ansy.num<<endl;
// merge(ansx,ansx,ansy);
return ansx.num+ansy.num-(ansx.lc==ansy.lc)-1;//ansx.num-1;
}
void start(){
for(int i=1;i<=n;i++)dfn[i]=i;
tim=n;
build(1,1,n);
// cout<<t[1].num<<endl;
// cout<<"hhh"<<endl;
for(int i=1;i<=q;i++){
op=read();
x=read();
y=read();
if(op==1){唯有牡丹真国色,花开时节动京城。
tim++;
modi(x,y);
}
else{
printf("%d\n",que(x,y));
}
}
return ;
}
}
int main(){
// freopen("ex_tree3.in","r",stdin);
// freopen("my.out","w",stdout);
n=read();
for(int i=1;i<=n-1;i++){
x=read();
y=read();
add(x,y);
add(y,x);
}
dfs(1);
dfs1(1,1);
q=read();
// if(n<=1000)p1::start();
// else
p2::start();
return 0;
}

\(\color{white}{\mathbb{唯有牡丹真国色,开花时节动京城。}}\)

noip模拟39的更多相关文章

  1. Noip模拟39 2021.8.14

    T1 打地鼠 都切掉了的简单题 1 #include<bits/stdc++.h> 2 #define int long long 3 using namespace std; 4 con ...

  2. 2021.8.14考试总结[NOIP模拟39]

    T1 打地鼠 全场就俩人没切,还有一个是忘关$freopen$了. $code:$ 1 #include<bits/stdc++.h> 2 #define rin register sig ...

  3. NOIP模拟 39

    考的嘛也不是. 伤心(怎么可能) T1稍想想组合数,然后牢记: 取模题随时取模,包括刚刚读入的数据  T2想到了基环树,然而不会打QAQ.. 非常简洁但非常大神的做法:随便断掉环上的一条边 利用“这条 ...

  4. NOIP模拟39:树

      他们说这题与之前树剖的一道叫染色的题类似,好像真的是这样.   就是我们考虑这样一件事,就是每一次染白都可以看作是给链上的点打一个时间戳,那么可以发现,如果相邻的两个点的时间戳不同,那么他们之间的 ...

  5. [考试总结]noip模拟39

    不写那么多没用的了 开题就发现 \(T4\) 原题, \(T1\) 大水题. 然后发现 \(T4\) 忘了.... 不扯了 打地鼠 大水题,我代码都不想放... 算了,还是放一下吧.. #includ ...

  6. NOIP模拟17.9.22

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

  7. NOIP 模拟4 T2

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

  8. noip模拟33

    \(\color{white}{\mathbb{失足而坠千里,翻覆而没百足,名之以:深渊}}\) 这场考试的时间分配非常不科学 开题试图想 \(t1\) 正解,一个半小时后还是只有暴力,特别惊慌失措 ...

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

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

随机推荐

  1. webrtc编译

    webrtc编译 最近研究 libmediasoupclient,而 libmediasoupclient 又依赖 libwebrtc,所以首先就想着先在windows上编译一个webrtc的库,先是 ...

  2. Matlab的datenum()函数用法

    Matlab的datenum()函数用法 将日期和时间转换为日期序列值 datenum 函数创建一个数值数组,将每个时间点表示为从 0000 年 1 月 0 日起的天数.数值还能表示以天为单位的过去时 ...

  3. 论文笔记:(TOG2019)DGCNN : Dynamic Graph CNN for Learning on Point Clouds

    目录 摘要 一.引言 二.相关工作 三.我们的方法 3.1 边缘卷积Edge Convolution 3.2动态图更新 3.3 性质 3.4 与现有方法比较 四.评估 4.1 分类 4.2 模型复杂度 ...

  4. 云服务器是什么?ECS、BCC、CVM...

    什么是云服务器?云服务器有哪些优势?能用来干什么? 很多人不太了解云服务器的定义和用途. 云服务器是一种简单高效.处理能力可弹性伸缩的计算服务,帮助用户快速构建更稳定.安全的应用,提升运维效率,降低 ...

  5. spring框架的学习->从零开始学JAVA系列

    目录 Spring框架的学习 框架的概念 框架的使用 Spring框架的引入 概念 作用 内容 SpringIOC的学习 概念 作用 基本使用流程 SpringIOC创建对象的三种方式 通过构造器方式 ...

  6. Excel VBA活动抽奖小程序

    在活动中,我们常会有抽奖,抽奖箱准备繁琐,现在多采用线上抽奖方式,下面用Excel VBA写了一个简单的抽奖小程序 简单测试效果如下,可实现: 多次抽奖,且每次抽奖都不重复 抽奖界面滚动人员信息,点击 ...

  7. kali linux重启网卡失败:Job for networking.service failed because the control process exited with error code. See "systemctl status networking.service" and "journalctl -xe" for details. 问题排查

    linux菜鸡的时候,总是为了配置网络而烦恼,重启网卡的原因有很多,我这次是因为配置了固定IP[使用第三方工具连接]所以需要重启网卡,出现 Job for networking.service fai ...

  8. 大厂Android岗高频面试问题:说说你对Zygote的理解!

    前言 Zygote可以说是Android开发面试很高频的一道问题,但总有小伙伴在回答这道问题总不能让面试满意, 在这你就要搞清楚面试问你对Zygote的理解时,面试官最想听到的和其实想问的应该是哪些? ...

  9. MySQL-初见

    目录 初识MySQL 数据库基本命令 创建数据库表 数据库引擎 修改和删除表字段 MySQL数据管理 DML语言 DQL查询数据 单表查询 WHERE条件子句 模糊查询:比较操作符 联表查询 排序查询 ...

  10. let 及const

    ES5中的块级作用域 ES5中只有全局作用域和函数作用域,这样带来了很多的不便利,会出现内层变量被外层变量覆盖,循环体中的变量会暴露在全局,很多情况下需要自执行函数来私有化变量. ES6块级的作用域 ...