T1 Lesson5 !

解题思路

首先对于整张图求出拓扑序,然后顺着拓扑序其实也就是顺着边的方向,更新最长路,也就是从 1 节点到达这个节点的最长路。

然后再逆着拓扑序,反向求一下最长路,也就是从这个节点出发可以到达的最远距离。

然后爆扫每一个点,用 multiset 维护删掉这个点以及与它相连的边对最终答案造成的影响,更新答案。

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Failed"<<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=1e5+10,M=5e5+10,INF=1e18;
int T,n,m,top,ans,aid,sta[N],du[N],f[N],g[N];
int tot=1,head[N],nxt[M<<1],ver[M<<1];
multiset<int> se;
vector<int> v[N];
void add_edge(int x,int y)
{
ver[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
v[y].push_back(x);
du[y]++;
}
void topo_sort()
{
int pos=0;
for(int i=1;i<=n;i++)
if(!du[i])
sta[++top]=i;
while(top<n)
{
pos++;
for(int i=head[sta[pos]];i;i=nxt[i])
{
int to=ver[i]; du[to]--;
if(!du[to]) sta[++top]=to;
}
}
}
void solve()
{
tot=1; top=0; ans=INF; memset(head,0,sizeof(head)); memset(du,0,sizeof(du));
memset(f,0,sizeof(f)); memset(g,0,sizeof(g)); se.clear();
n=read(); m=read();
if(n==1) return printf("1 0"),void();
for(int i=1;i<=n;i++) vector<int>().swap(v[i]);
for(int i=1,x,y;i<=m;i++) x=read(),y=read(),add_edge(x,y);
topo_sort();
for(int i=1;i<=n;i++)
{
int x=sta[i]; f[x]=max(f[x],1ll);
for(int j=head[x];j;j=nxt[j])
f[ver[j]]=max(f[ver[j]],f[x]+1);
}
for(int i=n;i>=1;i--)
{
int x=sta[i]; g[x]=max(g[x],1ll);
for(int j=head[x];j;j=nxt[j])
g[x]=max(g[x],g[ver[j]]+1);
}
for(int i=1;i<=n;i++) se.insert(g[i]); se.insert(0);
for(int i=1;i<=n;i++)
{
int x=sta[i];
for(int j=0;j<v[x].size();j++)
{
int to=v[x][j];
se.erase(se.find(f[to]+g[x]));
}
se.erase(se.find(g[x]));
int temp=(*se.rbegin());
if(temp<ans) ans=temp,aid=x;
else if(temp==ans&&x<aid) aid=x;
for(int j=head[x];j;j=nxt[j])
{
int to=ver[j];
se.insert(g[to]+f[x]);
}
se.insert(f[x]);
}
printf("%lld %lld\n",aid,ans-1);
}
signed main()
{
freopen("johnny.in","r",stdin); freopen("johnny.out","w",stdout);
T=read(); while(T--) solve();
return 0;
}

T2 贝尔数

解题思路

矩阵快速幂+CRT+exgcd

模数是一个合数,并且只有 5 个比较小的质因数,因此我们可以选择对于每一个小的质因数求出答案用 CRT 进行合并。

假设当前处理的质因数为 \(p\) ,对于小于 \(p\) 的答案可以直接根据公式1算出,对于比较大的直接矩阵快速幂通过第二个柿子进行转移,每次转移 \(p-1\) 个。

初始矩阵设为 \(1\times p\) 的矩阵,假设第一个数是 \(x\) 那么 \(x\) 可以直接从 \(x+p-1\) 转移过来, 其它的就运用公式进行运算就好了。

假如 \(p=3\) 的话(当然题目中并不存在这个因子)单位矩阵就是这样: \(\begin{bmatrix}0 & 1 & 0\\\\0 & 1 & 1\\\\1 & 0 & 1\\\\\end{bmatrix}\)

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Failed"<<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=110,M=1e3+10,mod=95041567;//31*37*41*43*47
int T,n,m,mo,q[N],f[M][10],c[N][N][10],s[10],t[10],d[10]={0,31,37,41,43,47};
struct Squre
{
int a[50][50];
void clear(){memset(a,0,sizeof(a));}
Squre friend operator * (Squre x,Squre y)
{
Squre z;z.clear();
for(int i=1;i<=m;i++)
for(int j=1;j<=m;j++)
for(int k=1;k<=m;k++)
z.a[i][j]+=x.a[i][k]*y.a[k][j];
for(int i=1;i<=m;i++)
for(int j=1;j<=m;j++)
z.a[i][j]%=mo;
return z;
}
}e;
void power(Squre &x,int y)
{
while(y)
{
if(y&1) x=x*e;
e=e*e; y>>=1;
}
}
int exgcd(int a,int b,int &x,int &y)
{
if(!b) return x=1,y=0,a;
int d=exgcd(b,a%b,x,y);
int z=x; x=y; y=z-y*(a/b);
return d;
}
void init(int pos)
{
int lim=d[pos],x,y; f[0][pos]=f[1][pos]=1;
exgcd(mod/d[pos],d[pos],x,y); t[pos]=(x%d[pos]+d[pos])%d[pos];
for(int i=1;i<=lim*2;i++)
{
c[i][0][pos]=c[i][i][pos]=1;
for(int j=1;j<i;j++)
c[i][j][pos]=(c[i-1][j][pos]+c[i-1][j-1][pos])%lim;
}
for(int i=2;i<=lim*2;i++)
for(int j=0;j<i;j++)
f[i][pos]=(f[i][pos]+c[i-1][j][pos]*f[j][pos])%lim;
}
void prework(int x)
{
e.clear(); e.a[x][1]=1;
for(int i=2;i<=x;i++)
e.a[i-1][i]=e.a[i][i]=1;
}
signed main()
{
freopen("bell.in","r",stdin); freopen("bell.out","w",stdout);
T=read();
for(int i=1;i<=T;i++) q[i]=read(),n=max(n,q[i]);
for(int i=1;i<=5;i++) init(i);
for(int i=1;i<=T;i++)
{
for(int j=1;j<=5;j++)
{
prework(d[j]); Squre x; x.clear();
m=mo=d[j];
int y=q[i]/(d[j]-1),res=q[i]%(d[j]-1);
for(int k=1;k<=d[j];k++)
x.a[1][k]=f[res+k-1][j];
power(x,y); s[j]=x.a[1][1];
}
int ans=0;
for(int j=1;j<=5;j++)
ans=(ans+s[j]*(mod/d[j])%mod*t[j]%mod)%mod;
printf("%lld\n",ans);
}
return 0;
}

T3 穿越广场

解题思路

AC自动机优化 DP

通过 AC 自动机 Fail 指针的定义可以知道,如果节点 u 的 Fail 指针指向 v ,那么 v 到根节点的串一定是 u 到根节点串的最长后缀。

那么我们对于一个节点一直跳下去每一个通过节点如果是两个串中某一个的结尾,那么相应的这个节点到根节点的路径中也就是包含了这个串。

如果一个节点最后没有子节点了他就一定会跳回根节点并且保留有之前走过的串的信息。

设 \(f_{i,j,k,sta}\) 表示一共走了 \(i\) 步,其中有 \(j\) 个 D ,当前在 \(k\) 节点 包含串的状态为 \(sta\) 的方案。

转移方程就是:

(f[i+1][j+1][tre[k][0]][sta|ending[tre[k]['D']]]+=f[i][j][k][sta])%=mod,
(f[i+1][j][tre[k][1]][sta|ending[tre[k]['R']]]+=f[i][j][k][sta])%=mod;

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Failed"<<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=110,mod=1e9+7;
int n,m,T,all,ans,ending[N<<1],tre[N<<1][2],f[N<<1][N][N<<1][4],fail[N<<1];
char ch1[N],ch2[N];
queue<int> q;
int insert(char ch[])
{
int len=strlen(ch+1),x=1;
for(int i=1;i<=len;i++)
{
int son=(ch[i]=='D');
if(!tre[x][son]) tre[x][son]=++all;
x=tre[x][son];
}
return x;
}
void get_Fail()
{
q.push(1); tre[0][0]=tre[0][1]=1;
while(!q.empty())
{
int x=q.front(); q.pop();
for(int i=0;i<=1;i++)
{
int son=tre[x][i];
if(son) fail[son]=tre[fail[x]][i],q.push(son);
else tre[x][i]=tre[fail[x]][i];
}
}
}
void solve()
{
all=1; ans=0; memset(tre,0,sizeof(tre)); memset(f,0,sizeof(f));
memset(fail,0,sizeof(fail)); memset(ending,0,sizeof(ending));
n=read(); m=read(); scanf("%s%s",ch1+1,ch2+1);
ending[insert(ch1)]|=1; ending[insert(ch2)]=2; f[0][0][1][0]=1; get_Fail();
for(int i=1;i<=all;i++) for(int j=i;j;j=fail[j]) ending[i]|=ending[j];
for(int i=0;i<n+m;i++)
for(int j=0;j<=n;j++)
for(int k=1;k<=all;k++)
for(int sta=0;sta<4;sta++)
(f[i+1][j+1][tre[k][0]][sta|ending[tre[k][0]]]+=f[i][j][k][sta])%=mod,
(f[i+1][j][tre[k][1]][sta|ending[tre[k][1]]]+=f[i][j][k][sta])%=mod;
for(int i=0;i<=all;i++) ans=(ans+f[n+m][n][i][3])%mod;
printf("%lld\n",ans);
}
signed main()
{
freopen("square.in","r",stdin); freopen("square.out","w",stdout);
T=read(); while(T--) solve();
return 0;
}

T4 舞动的夜晚

解题思路

最大流+Tarjan

先对于整个图跑一次最大流也就是最大匹配,如果对于最大匹配这个点已经用过了那么用它一定不会对答案造成影响。

对于最大流中没有用到的边建正边,用到的建反边,跑一边 Tarjan 如果一个边所连接的两个点在同一个强连通分量里。

表明这条边是可以替代别的边的,也可以理解为可以退流,因此不是我们要的答案。

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Failed"<<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=2e4+10,M=3e5+10,INF=1e18;
int tot=1,head[N],nxt[M<<1],ver[M<<1],edge[M<<1];
int n,m,t,fro,tim,top,cnt,Ans,sta[N<<1],low[N],bel[N],dfn[N],ending,Hd[N],dep[N];
bool ans[M],vis[N],b[N];
struct Road{int l,r;}pat[M];
void add_edge(int x,int y,int val)
{
ver[++tot]=y;
edge[tot]=val;
nxt[tot]=head[x];
head[x]=tot;
}
inline bool bfs()
{
memcpy(head,Hd,sizeof(Hd)); memset(dep,0x3f,sizeof(dep));
queue<int> q; while(!q.empty()) q.pop();
q.push(fro); dep[fro]=0;
while(!q.empty())
{
int x=q.front(); q.pop();
for(int i=head[x];i;i=nxt[i])
{
int to=ver[i];
if(dep[to]<=dep[x]+1||!edge[i]) continue;
q.push(to); dep[to]=dep[x]+1;
if(to==ending)return true;
}
if(x==ending) return true;
}
return false;
}
int dfs(int x,int in)
{
if(x==ending) return in;
int res=in;
for(int i=head[x];i;head[x]=i=nxt[i])
if(edge[i])
{
int to=ver[i];
if(dep[to]==dep[x]+1)
{
int tmp=dfs(to,min(res,edge[i]));
if(!tmp) dep[to]=0;
else edge[i]-=tmp,edge[i^1]+=tmp,res-=tmp;
}
if(!res) break;
}
return in-res;
}
void Tarjan(int x)
{
dfn[x]=low[x]=++tim; sta[++top]=x; b[x]=true;
for(int i=head[x];i;i=nxt[i])
{
int to=ver[i];
if(!dfn[to]) Tarjan(to),low[x]=min(low[x],low[to]);
else if(b[to]) low[x]=min(low[x],dfn[to]);
}
if(low[x]==dfn[x])
{
cnt++; int temp;
do
{
temp=sta[top--];
b[temp]=false;
bel[temp]=cnt;
}while(x!=temp);
}
}
signed main()
{
freopen("night.in","r",stdin); freopen("night.out","w",stdout);
n=read(); m=read(); t=read(); fro=0; ending=n+m+1;
for(int i=1,x,y;i<=t;i++)
x=read(),y=read(),pat[i]=(Road){x,y},
add_edge(x,y+n,1),add_edge(y+n,x,0);
for(int i=1;i<=n;i++) add_edge(fro,i,1),add_edge(i,fro,0);
for(int i=1;i<=m;i++) add_edge(i+n,ending,1),add_edge(ending,i+n,0);
memcpy(Hd,head,sizeof(head)); while(bfs()) dfs(fro,INF);
for(int i=1;i<=t;i++) ans[i]=edge[i<<1]^1;
for(int i=1;i<=n;i++) vis[i]=edge[(t+i)<<1]^1;
for(int i=1;i<=m;i++) vis[n+i]=edge[(t+n+i)<<1]^1;
tot=1;
for(int i=1;i<=n+m+2;i++) memset(head,0,sizeof(head));
for(int i=1;i<=t;i++)
if(!ans[i]) add_edge(pat[i].l,pat[i].r+n,0);
else add_edge(pat[i].r+n,pat[i].l,0);
for(int i=1;i<=n;i++)
if(!vis[i]) add_edge(fro,i,0);
else add_edge(i,fro,0);
for(int i=1;i<=m;i++)
if(!vis[i+n]) add_edge(i+n,ending,0);
else add_edge(ending,i+n,0);
Tarjan(fro);
for(int i=1;i<=n+m;i++)
if(!dfn[i])
Tarjan(i);
if(!dfn[ending]) Tarjan(ending);
for(int i=1;i<=t;i++)
if(bel[pat[i].l]==bel[pat[i].r+n])
ans[i]=true;
for(int i=1;i<=t;i++) Ans+=ans[i]^1;
printf("%lld\n",Ans);
for(int i=1;i<=t;i++)
if(!ans[i])
printf("%lld ",i);
return 0;
}

NOIP模拟58的更多相关文章

  1. Noip模拟58 2021.9.21(中秋祭&&换机房祭)

    第一次在学校过中秋节,给家里人视频电话,感觉快回家了很开心, 然后还吃了汉堡喝饮料非常爽,颓废了一会儿还换了新机房,$Linux2.0$非常dei,少爷机也非常快, 发现好像测评机又成了老爷机,这就是 ...

  2. 2021.9.21考试总结[NOIP模拟58]

    T1 lesson5! 开始以为是个无向图,直接不懂,跳去T2了. 之后有看了一眼发现可暴力,于是有了\(80pts\). 发现这个图是有拓扑序的,于是可以用拓扑排序找最长路径.先找原图内在最长路径上 ...

  3. CH Round #58 - OrzCC杯noip模拟赛day2

    A:颜色问题 题目:http://ch.ezoj.tk/contest/CH%20Round%20%2358%20-%20OrzCC杯noip模拟赛day2/颜色问题 题解:算一下每个仆人到它的目的地 ...

  4. NOIp模拟赛二十八

    (这是NOIp模拟赛?应该是NOI模拟赛不小心加了个p) 嗯,假装这是正经的NOIp模拟赛,从今天开始也写写题解吧(这几天被虐的惨惨) 今日情况:8+50+0=58 A题输出样例,B题正解写挂,C题不 ...

  5. NOIP模拟 1

    NOIP模拟1,到现在时间已经比较长了.. 那天是6.14,今天7.18了 //然鹅我看着最前边缺失的模拟1,还是终于忍不住把它补上,为了保持顺序2345重新发布了一遍.. #   用  户  名   ...

  6. NOIP模拟17.9.22

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

  7. NOIP 模拟4 T2

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

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

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

  9. NOIP模拟赛20161022

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

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

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

随机推荐

  1. Pytorch-tensor的激活函数

    1.激活函数 激活函数的作用是能够给神经网络加入一些非线性因素,使得神经网络可以更好地解决较为复杂的问题.因为很多问题都不是线性的,你只有给它加入一些非线性因素,就能够让问题更好的解决. 函数1:RE ...

  2. 重磅:FPGA实现MIPI DSI4线720P

    1. 液晶屏概述 显示屏LCD MIPI DSI4 lane,支持分辨率720*1280,60HZ彩色显示.用于对接国产GOWIN的NR-9C的开发板和LATTICE的CROSSLINK开发板,显示M ...

  3. locust分布式压测的Step Load及no web模式下的报表自动生成

    Running Locust in Step Load ModeIf you want to monitor your service performance with different user ...

  4. Vue——自动切换图片

    利用属性指令 + setInterval(是一个实现定时调用的函数) <!DOCTYPE html> <html lang="en"> <head&g ...

  5. Django框架——ajax补充、多对多三种创建、序列化组件、批量操作数据、分页器

    ajax补充说明 主要是针对回调函数args接收到的响应数据 1.后端request.is_ajax() 用于判断当前请求是否由ajax发出 2.后端返回的三板斧都会被args接收不再影响整个浏览器页 ...

  6. 即学即会 Serverless | 初识 Serverless

    简介:Serverless 架构被越来越多的业务所采纳,成为其技术选型,大多数开发者已经跨越对 Serverless 概念了解,切实向落地实践出发.本文带大家一探究竟,为什么说 Serverless ...

  7. 阿里云 Serverless 助力企业全面拥抱云原生

    ​简介:相信随着云计算的发展,Serverless 将成为云时代默认的计算范式,越来越多的企业客户将会采用这个技术. 作者:洛浩 Serverless 应用引擎的组件架构 最早的时候,大家设计软件一般 ...

  8. [Blockchain] (Binance Smart Chain) BSC 测试网 BNB 水龙头

    测试网BNB水龙头 https://testnet.binance.org/faucet-smart 测试网区块浏览器 https://testnet.bscscan.com 主网区块浏览器 http ...

  9. [FE] uni-app 导航栏开发指南

    一种是 原生导航栏添加自定义按钮.简单明了. pages.json 配置 { "path": "pages/log/log", "style" ...

  10. [FAQ] JS 实现暂停(睡眠) Sleep 与 倒计时 ?

    想要暂停/睡眠一秒,可以参考使用以下方式: async () => { await (new Promise((resolve) => setTimeout(resolve, 1000)) ...