LOADING

加载过慢请开启缓存 浏览器默认开启

叫兽の窝

搭建hexo个人博客

杂谈 2024/8/29

踩了很多坑,修修补补才搭起来,写一篇教程以防自己以后再遇到各种各样的问题

一.准备工作

1.魔法上网

2.github账号,(gitee理论上也可以,没试过)

3.一台可以开关机的电脑和一颗有探索精神的大脑

二.安装软件

1.安装git

​ windows:到git官网上下载,之后会有一个Git Bash的命令行工具,以后就用这个工具来使用git。

image-20240830205923782

2.安装nodejs

​ windows:nodejs选择LTS版本就行了。

image-20240830210014525

安装完之后试着用cmd或git bash输入指令检查一遍有没有安装成功

node -v
npm -v

3.安装hexo(博客框架)

​ 在硬盘任意地方新建一个文件夹,用来存放你的博客本地文件和后续写的文章等等,在这个文件夹下右键选择git bash打开

输入指令

npm install -g hexo-cli

接着初始化文件夹

hexo init <你的文件夹名字>
例如文件夹名为myblog:
hexo init myblog

安装npm

npm install

这时你的文件夹内会出现很多文件,这些就是hexo建站所依赖的各种包,但还没完

hexo g //生成静态文件
hexo s //启动服务

这时git base窗口会出现一串网址:http://localhost:4000 ,这时代表本地建站成功,打开浏览器输入网址可以看到网站默认主页e6604cfe09af32577d116c18aa7a14c1

![image-20201220204625063](https://i-blog.csdnimg.cn/blog_migrate/e6604cfe09af32577d116c18aa7a14c1.png

到这一步已经实现一半了,但目前只能在本地访问你的博客,怎么让他在互联网上访问呢?

三.部署github

1.创建仓库

​ 打开你的github,创建一个和你用户名相同的仓库,后面加.github.io,只有这样,将来要部署到GitHub page的时候,才会被识别,也就是xxxx.github.io,其中xxx就是你注册GitHub的用户名。

2.生成所需的ssh

在git base中输入

git config --global user.name "yourname"
git config --global user.email "youremail"

按提示一路回车,之后输入

ssh-keygen -t rsa -C "youremail"

​ 复制出现的以ssh开头的字符串,接下来回到github中,点击右上角头像里的setting选项,找到SSH keys,选择New SSH key,随便起一个title,在下方key中输入你刚刚复制的密钥,回到git base,输入

ssh -T git@github.com

​ 检查是否配置成功,当出现successfully字样代表成功,但还有最后一步!

3.发布

还是git base窗口,输入

npm install hexo-deployer-git

这个插件用于把生成好的静态页面上传到 GitHub Pages 仓库,接着要告诉hexo 你的文件要发布到哪个仓库

点击根目录下的_config.yaml文件,翻到最下边,修改以下指令

deploy:
  type: git
  repo: git@github.com:xxxxxx/xxxxxx.github.io.git
  branch: main

保存,回到git base中,输入

hexo g && hexo d

等待上传文件,之后输入https://你的用户名.github.io,你会发现大功告成!

阅读全文

'test'

2024/12/25
阅读全文

'test'

2024/12/23
阅读全文

拓扑思想的运用

C++ 2024/10/26

ICPC第13届山东省赛

题面

image-20241026175921438

思路

模拟一下拓扑序列的思想,把需求抽象成一种边,每一种尚未满足的需求都是一个入度,按顺序把解决的需求对应的项目入度减一,定义一个队列,把所有入度为0的项目全部加进去,当这个队列为空时,所有可解决的项目均被记录,退出循环输出答案即可

#include<bits/stdc++.h>
using namespace std;
#define int long long
constexpr int inf32 = 1e9 + 7;
constexpr int inf64 = 1e9 + 7;
const int N=2e5;
typedef pair<int,int> PII;
map<int,int>have;                                           //当前有的员工的对应数量
map<int,priority_queue<PII,vector<PII>,greater<PII>>> need; //所有需求,及对应的任务标号,用优先队列存
vector<PII>reward[N];                                       //解决某个任务之后获得的奖励人数
int din[N];
int x,n;
int ans;                    
void solve(){
    cin>>x;
    for(int i=1;i<=x;i++){
        int a,b;
        cin>>a>>b;
        have[a]+=b;
    }
    cin>>n;
    queue<int>accord;
    for(int i=1;i<=n;i++){
        int m1,m2;
        cin>>m1;                            //第i项目有m1个需求
        for(int j=1;j<=m1;j++){     
            int a,b;    
            cin>>a>>b;    
            if(have[a]<b){                  //只有当前的需求尚未被满足,才增加一个入度
                din[i]++;
                need[a].push({b,i});        //员工标号为a的增加一个数量为b的需求,该需求对因项目i
            }    
        }
        cin>>m2;                    
        for(int j=1;j<=m2;j++){
            int a,b;
            cin>>a>>b;
            reward[i].push_back({a,b});     //reward[i]表示解决项目i后获得的所有奖励
        }
        if(!din[i]) accord.push(i);         //若入度为0则已经被解决
    }
    while(!accord.empty()){                 //已经被解决的条件
        int idx=accord.front();
        accord.pop();
        ans++;
        for(auto [id,num]:reward[idx]){     //遍历得到的新员工可以解决的需求,若出现新入度为0的点,加入队列
            have[id]+=num;
            while(!need[id].empty()){
                if(need[id].top().first<=have[id]){
                    din[need[id].top().second]--;
                    if(din[need[id].top().second]==0) accord.push(need[id].top().second);
                    need[id].pop();
                }
                else break;
            }
        }
    }
    cout<<ans<<endl;
}
signed main(){
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
    int t=1;
    //cin>>t;
    while(t--){
        solve();
    }
}
阅读全文

tarjan缩点后求最短路

算法 2024/10/24

正则表达式

题目背景

小Z童鞋一日意外的看到小X写了一个正则表达式的高级程序,这个正则表达式程序仅仅由字符“0”,“1”,“.”和“*”构成,但是他能够匹配出所有在OJ上都AC的程序的核心代码!小Z大为颇感好奇,于是他决定入侵小X的电脑上去获得这个正则表达式的高级程序。

题目描述

在 Internet 网络中的每台电脑并不是直接一对一连通的,而是某些电脑之间存在单向的网络连接,也就是说存在 $A$ 到 $B$ 的连接不一定存在 $B$ 到 $A$ 的连接,并且有些连接传输速度很快,有些则很慢,所以不同连接传输所花的时间是有大有小的。另外,如果存在 $A$ 到 $B$ 的连接的同时也存在 $B$ 到 $A$ 的连接的话,那么 $A$ 和 $B$ 实际上处于同一局域网内,可以通过本地传输,这样花费的传输时间为 $0$。

现在小 Z 告诉你整个网络的构成情况,他希望知道从他的电脑(编号为 $1$),到小 X 的电脑(编号为 $n$)所需要的最短传输时间。

输入格式

第一行两个整数 $n,m$,表示有 $n$ 台电脑,$m$ 个连接关系。

接下来 $m$ 行,每行三个整数 $u,v,w$,表示从电脑 $u$ 到电脑 $v$ 传输信息的时间为 $w$。

输出格式

输出文件仅一行为最短传输时间。

样例输入

5 5
1 2 1
2 3 6
3 4 1
4 2 1
3 5 2

样例输出

3

实现

​ 简述一下题目,存在一有向有环图,在任意强连通块里面的路径权值均可看作0,求1号点到n号点最短路。

​ 在tarjan缩点得到的有向无环图上跑一遍Dijkstra即可得到答案

#include<bits/stdc++.h>
using namespace std;
#define int long long
constexpr int inf32 = 1e9 + 7;
constexpr int inf64 = 1e9 + 7;
const int N=2e5;
struct tree{
    int to;
    int w;
};
vector<tree>e[N];
vector<tree>e_dist[N];
int cnt,idx,num;
int dfn[N],instk[N],low[N];
int belong[N];
stack<int> stk;
vector<int>scc[N];
int dout[N];
typedef pair<int,int> PII;
void tarjan(int u){
    dfn[u]=low[u]=++idx;
    stk.push(u);
    instk[u]=1;
    for(auto V:e[u]){
        int v=V.to;
        if(!dfn[v]){
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(instk[v]){
            low[u]=min(low[u],dfn[v]);
        }
    }
    if(low[u]==dfn[u]){
        cnt++;
        int v;
        do{
            v=stk.top();
            stk.pop();
            instk[v]=0;
            scc[cnt].push_back(v);
            belong[v]=cnt;
        }while(v!=u);
    }
}
int dist[N];
void dijkstra(int u){
    priority_queue<PII,vector<PII>,greater<PII>> q;
    dist[u]=0;
    q.push({0,u});
    while(!q.empty()){
        auto [distance,k]=q.top();
        q.pop();
        for(auto V:e_dist[k]){
            int v=V.to;
            int w=V.w;
            if(dist[v]>distance+w){
                dist[v]=distance+w;
                q.push({dist[v],v});
            }
        }
    }
}
void solve(){
    int n,m;
    cin>>n>>m;
    memset(dist,127,sizeof(dist));
    for(int i=1;i<=m;i++){
        int u,v,w;
        cin>>u>>v>>w;
        e[u].push_back({v,w});
    }
    for(int i=1;i<=n;i++){
        if(!dfn[i]) tarjan(i);
    }
    for(int i=1;i<=cnt;i++){
        for(auto u:scc[i]){
            for(auto v:e[u]){
                e_dist[i].push_back({belong[v.to],v.w});
            }
        }
    }
    dijkstra(belong[1]);
    cout<<dist[belong[n]]<<endl;
}
    
signed main(){
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
    int t=1;
    //cin>>t;
    while(t--){
        solve();
    }
}
阅读全文

kruskal重构并倍增求路径最小值

算法 2024/10/24

[NOIP2013 提高组] 货车运输

题目背景

NOIP2013 提高组 D1T3

题目描述

A 国有 $n$ 座城市,编号从 $1$ 到 $n$,城市之间有 $m$ 条双向道路。每一条道路对车辆都有重量限制,简称限重。

现在有 $q$ 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。

输入格式

第一行有两个用一个空格隔开的整数 $ n,m$,表示 A 国有 $ n$ 座城市和 $m$ 条道路。

接下来 $m$ 行每行三个整数 $x, y, z$,每两个整数之间用一个空格隔开,表示从 $x $ 号城市到 $ y $ 号城市有一条限重为 $z$ 的道路。
注意: $x \neq y$,两座城市之间可能有多条道路 。

接下来一行有一个整数 $q$,表示有 $q$ 辆货车需要运货。

接下来 $q$ 行,每行两个整数 $x,y$,之间用一个空格隔开,表示一辆货车需要从 $x$ 城市运输货物到 $y$ 城市,保证 $x \neq y$

输出格式

共有 $q$ 行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。
如果货车不能到达目的地,输出 $-1$。

样例输入 #1

4 3
1 2 4
2 3 3
3 1 1
3
1 3
1 4
1 3

样例输出 #1

3
-1
3

实现

​ 最初的想法是tarjan缩点,去环后倍增求最小值,仔细思考后发现是完全没有道理的,无法在多条路线里面找到最优的路线,所以转变为用生成树重构图确定唯一的路线后再去倍增求最小值

#include<bits/stdc++.h>
using namespace std;
#define int long long
constexpr int inf32 = 1e9 + 7;
constexpr int inf64 = 1e9 + 7;
const int N=2e5+10;
typedef pair<int,int> PII;
int n,m;
struct tree{
    int u,v,w;
    bool operator< (const tree &t) const{
        return w>t.w;
    }
}edge[N];
vector<PII>e[N];
int p[N],ans,cnt;
int find(int x){
    if(x!=p[x]) p[x]=find(p[x]);
    return p[x];
}
int per[N][32];
int val[N][32];
int dep[N];
int st[N];
void dfs(int u,int f){
    dep[u]=dep[f]+1;
    st[u]=1;
    for(auto [v,w]:e[u]){
        if(v==f) continue;
        per[v][0]=u;
        val[v][0]=w;
        dfs(v,u);
    }
}
void kruskal(){
    sort(edge+1,edge+1+m);          //根据边权排序,由大到小枚举
    for(int i=1;i<=m;i++){
        int x=find(edge[i].u);
        int y=find(edge[i].v);
        if(x!=y){                   //若父节点与子节点在同一集合则不可连边,否则出现环
            e[edge[i].u].push_back({edge[i].v,edge[i].w});
            e[edge[i].v].push_back({edge[i].u,edge[i].w});
            p[x]=y;
            ans+=edge[i].w;
            cnt++;                  //重构后的树点的个数加一
        }
    }
}
int query(int u,int v){             //倍增lca求路径最小值
    int ans=1e9;
    if(dep[u]>dep[v]) swap(u,v);
    int d=dep[v]-dep[u];
    for(int j=20;j>=0;j--){
        if(d&(1<<j)){
            ans=min(ans,val[v][j]);
            v=per[v][j];
        }
    }
    if(u==v){
        return ans;
    }
    for(int j=20;j>=0;j--){
        if(per[u][j]!=per[v][j]){
            ans=min(ans,min(val[u][j],val[v][j]));
            u=per[u][j];
            v=per[v][j];
        }
    }
    ans=min(ans,min(val[u][0],val[v][0]));
    return ans;
}
void solve(){
    //int n,m;
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        int u,v,w;
        cin>>u>>v>>w;
        edge[i]={u,v,w};
    }
    for(int i=1;i<=n;i++){
        p[i]=i;
    }
    kruskal();
    for(int i=1;i<=n;i++){
        if(!st[i]) dfs(i,0);
    }
    //dfs(1,0);
    for(int j=1;j<32;j++){          //构建st表
        for(int i=1;i<=n;i++){
            per[i][j]=per[per[i][j-1]][j-1];
            val[i][j]=min(val[i][j-1],val[per[i][j-1]][j-1]);
        }
    }
    int q;
    cin>>q;
    for(int i=1;i<=q;i++){
        int u,v;
        cin>>u>>v;
        if(find(u)!=find(v)){
            cout<<-1<<endl;
        }
        else{
            cout<<query(u,v)<<endl;
        }
    }
}    
signed main(){
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
    int t=1;
    //cin>>t;
    while(t--){
        solve();
    }
}
阅读全文

换根dp求距离和+倍增lca求链长

算法 2024/10/24

题目描述

本题和$hard$难度的区别是,询问的次数有多次!

小红拿到了一棵树,她有多次询问,每次询问输入一条简单路径$x,y$,她想知道树上所有节点到该路径的最短路之和是多少,你能帮帮她吗?

定义节点到路径的最短路为:节点到路径上所有点的最短路中,值最小的那个。特殊的,如果节点在路径上,则最短路为0。

简单路径:从树上的一个节点出发,沿着树的边走,不重复地经过树上的节点,到达另一个节点的路径。

第一行输入两个正整数$n,q$,代表节点数量和询问次数。
接下来的$n-1$行,每行输入两个正整数$u,v$,代表节点$u$和节点$v$有一条边连接。
接下来的$q$行,每行输入两个正整数$x,y$,代表一次询问。
$1\leq n,q \leq 10^5$
$1\leq u,v,x,y \leq n$

输入

4 2
1 2
1 3
1 4
2 3
2 1

输出

1
2

实现

​ 并没有看懂关于所有点到链的距离和即所有点到左右端点的距离和加上链长的证明,但有了这个结论就比较好写了,比较板的换根dp求距离和加上倍增lca求链长。

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e6 + 10;
const int M = 5e3 + 10;
vector<int>e[N];
int V[N],f[N],Size[N];
bool st[N];
int dep[N],fa[N],par[N][33];
void up(int u){
    Size[u]=1;
    st[u]=1;
    for(int v:e[u]){
        if(st[v]) continue;
        fa[v]=u;
        par[v][0]=u;
        up(v);
        Size[u]+=Size[v];
        f[u]+=f[v];
    }
    f[u]+=Size[u]-1;
    st[u]=0;
}
void down(int u){
    st[u]=1;
    for(int v:e[u]){
        if(st[v]) continue;
        dep[v]=dep[u]+1;
        V[v]=V[u]+f[u]-f[v]+Size[1]-Size[v]-Size[v];
        down(v);
    }
}
int lca(int u,int v){
    if(dep[u]<dep[v]) swap(u,v);
    while(dep[u]!=dep[v]){
        u=fa[u];
    }
    int d=dep[u]-dep[v];
    if(u==v) return u;
    for(int j=20;j>=0;j--){
        if(par[u][j]!=par[v][j]){
            u=par[u][j];
            v=par[v][j];
        }
    }
   return par[u][0];
}
void solve()
{   
    int n,q;
    cin>>n>>q;
    for(int i=1;i<=n-1;i++){
        int u,v;
        cin>>u>>v;
        e[u].push_back(v);
        e[v].push_back(u);
    }
    dep[1]=1;
    up(1);
    down(1);
    for(int i=1;i<=21;i++){
        for(int j=1;j<=n;j++){
            if(par[j][i-1]!=0)
            par[j][i]=par[par[j][i-1]][i-1];
        }
    }
    for(int i=1;i<=q;i++){
        int u,v;
        cin>>u>>v;
        int dist1=V[u]+f[u];
        int dist2=V[v]+f[v];
        int dist3=dep[u]+dep[v]-2*dep[lca(u,v)];
        cout<<(dist1+dist2-n*dist3)/2<<endl;
    }
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t = 1;
    //cin >> t;
    while (t--)
    {
        solve();
    }
} 
阅读全文

换根dp

算法 2024/9/26

树的最长路径

题目

image-20240926193757033

思路

这道题的前置知识点可以参考[树的最长路径](1072. 树的最长路径 - AcWing题库)easy版,核心代码如下:

void up(int u,int fa){
    st[u]=1;
    for(auto v:e[u]){
        if(v.to==fa) continue;
        up(v.to,u);
        if(f[u][1]<=f[v.to][1]+v.w){
            f[u][2]=f[u][1];
            f[u][1]=f[v.to][1]+v.w;
        }
        else if(f[u][2]<=f[v.to][1]+v.w){
            f[u][2]=f[v.to][1]+v.w;
        }
    }
}

以某点为根的子树的最长路径即为以该点为根的最长路与次长路的长度和,一遍dfs即可求出所有子树的最长路径,但在这个题里,显然某点的最长路径不一定存在于某点的子树中,而存在于该点的根节点里面,这个时候就需要引入换根的概念了

img

​ 如图给出了一颗树,当以1为根时,显然他的最长直径为路径1-3-7-11(f[1] [2]=3,一维表示以1为根,二维表示第2长的路)与路径1-4-9-13-15(f[1] [1]=4)的长度和7.

​ 但当我们考虑结点4时,第一遍dfs跑出来的结果p[4] [1]=3,p[4] [2]=2,显然不是最优的,因为把结点1看作他的子节点时,f[4] [1]可以被f[1] [2]+1替换,同理f[3] [1]可以被f[1] [1]+1替换,f[3] [2]继承 f[3] [1]。

​ 如果此时我们对每个节点都跑一遍dfs的话可以解决这个问题,但其时间复杂度为O(n^2) 的,显然不符合题面的要求,所有需要考虑怎么在O(n)的时间复杂度下得出每点的答案,这个时候对每个结点观察可以看出,答案是否需要更新只和他的根节点信息有关,当根节点长度大于p[i][1]或者p[i][2]时按需求更新答案即可。

​ 具体的更新答案的情况有两类,一种是子节点v被父节点u的第一长路径覆盖,另一种则是被第二长路径覆盖或不被覆盖。

​ 当我们考虑第一种情况是,对应图中的v=4号结点,当前结点v被根节点u的第一长的路径包括在内,v的第一长的路径长度一定是最优的,但需要考虑f[v] [2]与f[u] [2]+1的关系。第二种情况对应v=2或者v=3,此时根节点u中一定存在一条最优的路径大于p[v] [1]与p[v] [2],因为根结点在寻找最长路径时没有考虑经过子节点v,所以子节点v中的任何路径都是非最优解,那么直接将p[v] [2]更新为p[v] [1],并用p[u] [1]+1替换p[v] [1]。

​ 注意在每一次更新都要维护当前的结点所选的每一条路都要经过的那个子节点,用来后续判断某个点是否为父节点所选

代码

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e6 + 10;
typedef pair<int,int> PII;
bool st[N];
struct tree{
    int to;
};
int size[N],f[N][3],p[N][3];
vector<tree> e[N];
void up(int u){
    st[u]=1;
    for(auto v:e[u]){
        if(st[v.to]) continue;
        up(v.to);
        if(f[u][1]<f[v.to][1]+1){
            f[u][2]=f[u][1];
            p[u][2]=p[u][1];
            f[u][1]=f[v.to][1]+1;
            p[u][1]=v.to;
        }
        else if(f[u][2]<f[v.to][1]+1){
            f[u][2]=f[v.to][1]+1;
            p[u][2]=v.to;
        }
    }
}
void down(int u){
    st[u]=1;
    for(auto v:e[u]){
        if(st[v.to]) continue;
        if(p[u][1]==v.to){
            if(f[v.to][1]<f[u][2]+1){
                f[v.to][2]=f[v.to][1];
                p[v.to][2]=p[v.to][1];
                f[v.to][1]=f[u][2]+1;
                p[v.to][1]=u;
            }
            else if(f[v.to][2]<f[u][2]+1){
                f[v.to][2]=f[u][2]+1;
                p[v.to][2]=u;
            }
        }
        else{
            f[v.to][2]=f[v.to][1];
            p[v.to][2]=p[v.to][1];
            f[v.to][1]=f[u][1]+1;
            p[v.to][1]=u;
        }
        down(v.to);
    }
}
void solve(){
    int n;
    cin>>n;
    for(int i=1;i<n;i++){
        int u,v;
        cin>>u>>v;
        e[u].push_back({v});
        e[v].push_back({u});
    }
    up(1);
    for(int i=1;i<=n;i++) st[i]=0;
    down(1);
    int ans=0;
    for(int i=1;i<=n;i++){
        cout<<f[i][1]+f[i][2]<<endl;
    }
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t=1;
    //cin >> t;
    while (t--) {
        solve();
    } 
}
阅读全文

AtCoder Beginner Contest 372

题解 2024/9/24

D - Buildings

Problem Statement

There are $N$ buildings, Building $1$, Building $2$, $\ldots$, Building $N$, arranged in a line in this order. The height of Building $i$ $(1 \leq i \leq N)$ is $H_i$.

For each $i = 1, 2, \ldots, N$, find the number of integers $j$ $(i < j \leq N)$ satisfying the following condition:

  • There is no building taller than Building $j$ between Buildings $i$ and $j$.

Constraints

  • $1 \leq N \leq 2 \times 10^5$
  • $1 \leq H_i \leq N$
  • $ H_i\neq H_j\ (i\neq j)$
  • All input values are integers.

Sample Input 1

5
2 1 4 3 5

Sample Output 1

3 2 2 1 0

For $i=1$, the integers $j$ satisfying the condition are $2$, $3$, and $5$: there are three. (Between Buildings $1$ and $4$, there is a building taller than Building $4$, which is Building $3$, so $j=4$ does not satisfy the condition.) Therefore, the first number in the output is $3$.

实现

倒着将元素挨个放入单调栈,栈的元素个数即为当前位置的答案

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5 + 10;
int a[N],b[N],c[N];
stack<int>st;
void solve(){
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    for(int i=n;i>=1;i--){     
        b[i]=st.size();
        while(!st.empty()&&a[i]>st.top()){
            st.pop();
        }
        st.push(a[i]);
    }
    for(int i=1;i<=n;i++){
        cout<<b[i]<<' ';
    }
    cout<<endl;
}
signed main() {
    int t=1;
    //cin >> t;
    while (t--) {
        solve();
    }
       
}

E - K-th Largest Connected Components

Problem Statement

There is an undirected graph with $N$ vertices and $0$ edges. The vertices are numbered $1$ to $N$.

You are given $Q$ queries to process in order. Each query is of one of the following two types:

  • Type $1$: Given in the format 1 u v. Add an edge between vertices $u$ and $v$.

  • Type $2$: Given in the format 2 v k. Print the $k$-th largest vertex number among the vertices connected to vertex $v$. If there are fewer than $k$ vertices connected to $v$, print -1.

Constraints

  • $1 \leq N, Q \leq 2 \times 10^5$
  • In a Type $1$ query, $1 \leq u < v \leq N$.
  • In a Type $2$ query, $1 \leq v \leq N$, $1 \leq k \leq 10$.
  • All input values are integers.

Sample Input 1

4 10
1 1 2
2 1 1
2 1 2
2 1 3
1 1 3
1 2 3
1 3 4
2 1 1
2 1 3
2 1 5

Sample Output 1

2
1
-1
4
2
-1
  • In the first query, an edge is added between vertices $1$ and $2$.
  • In the second query, two vertices are connected to vertex $1$: $1$ and $2$. Among them, the $1$-st largest vertex number is $2$, which should be printed.
  • In the third query, two vertices are connected to vertex $1$: $1$ and $2$. Among them, the $2$-nd largest vertex number is $1$, which should be printed.
  • In the fourth query, two vertices are connected to vertex $1$: $1$ and $2$, which is fewer than $3$, so print -1.
  • In the fifth query, an edge is added between vertices $1$ and $3$.
  • In the sixth query, an edge is added between vertices $2$ and $3$.
  • In the seventh query, an edge is added between vertices $3$ and $4$.
  • In the eighth query, four vertices are connected to vertex $1$: $1,2,3,4$. Among them, the $1$-st largest vertex number is $4$, which should be printed.
  • In the ninth query, four vertices are connected to vertex $1$: $1,2,3,4$. Among them, the $3$-rd largest vertex number is $2$, which should be printed.
  • In the tenth query, four vertices are connected to vertex $1$: $1,2,3,4$, which is fewer than $5$, so print -1.

实现

启发式合并+平衡树查询

#include <bits/stdc++.h>
#include <bits/extc++.h>
using namespace __gnu_cxx;
using namespace __gnu_pbds;
using namespace std;
#define int long long
typedef tree<int, null_type, greater<int>, rb_tree_tag, tree_order_statistics_node_update> Tree;
const int N = 2e6 + 10;
Tree tr[N];
int f[N];
int find(int x){
    return f[x]==x?x:f[x]=find(f[x]);
}
void solve(){
    int n,q;
    cin>>n>>q;
    for(int i=1;i<=n;i++){
        tr[i].insert(i);
        f[i]=i;
    }
    for(int i=1;i<=q;i++){
        int st;
        cin>>st;
        if(st==1){
            int u,v;
            cin>>u>>v;
            u=find(u),v=find(v);
            if(u==v) continue;
            if(tr[u].size()>tr[v].size()){
                swap(u,v);
            }
            for(auto it:tr[u]){
                tr[v].insert(it);
                f[it]=v;
            }
            tr[u].clear();
        }
        else{
            int v,k;
            cin>>v>>k;
            v=find(v);
            if(tr[v].size()>=k){
                cout<<*tr[v].find_by_order(k-1)<<endl;
            }
            else{
                cout<<-1<<endl;
            }
        }
    }
}
signed main(){
    int t=1;
    //cin >> t;
    while (t--) {
        solve();
    } 
}
阅读全文

单调队列

数据结构 2024/9/20

琪露诺

在幻想乡,琪露诺是以笨蛋闻名的冰之妖精。

某一天,琪露诺又在玩速冻青蛙,就是用冰把青蛙瞬间冻起来。但是这只青蛙比以往的要聪明许多,在琪露诺来之前就已经跑到了河的对岸。于是琪露诺决定到河岸去追青蛙。

小河可以看作一列格子依次编号为 $0$ 到 $N$,琪露诺只能从编号小的格子移动到编号大的格子。而且琪露诺按照一种特殊的方式进行移动,当她在格子 $i$ 时,她只移动到区间 $[i+L,i+R]$ 中的任意一格。你问为什么她这么移动,这还不简单,因为她是笨蛋啊。

每一个格子都有一个冰冻指数 $A_i$,编号为 $0$ 的格子冰冻指数为 $0$。当琪露诺停留在那一格时就可以得到那一格的冰冻指数 $A_i$。琪露诺希望能够在到达对岸时,获取最大的冰冻指数,这样她才能狠狠地教训那只青蛙。

但是由于她实在是太笨了,所以她决定拜托你帮它决定怎样前进。

开始时,琪露诺在编号 $0$ 的格子上,只要她下一步的位置编号大于 $N$ 就算到达对岸。

样例输入 #1

5 2 3
0 12 3 11 7 -2

样例输出 #1

11
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n' 
const int N=2e6;
int a[N],dp[N],s[N];
deque<int>dq1,dq2;
int n,h;
deque<int>p;         
void solve(){
    int n,l,r;
    cin>>n>>l>>r;
    for(int i=0;i<=n;i++){
        cin>>a[i];
    }
    memset(dp,-127,sizeof(dp));
    int j=0;
    dp[0]=0;
    for(int i=l;i<=n;i++){
        while(j<=i-l){
            while(!p.empty()&&dp[j]>=dp[p.back()]){
                p.pop_back();
            }
            p.push_back(j);
            j++;
        }
        if(!p.empty()&&p.front()<i-r) p.pop_front();
        dp[i]=dp[p.front()]+a[i];
    }
    int ans=-1e9;
    for(int i=n-r+1;i<=n;i++){
       ans=max(ans,dp[i]);
    }
    cout<<ans<<endl;

}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout << fixed << setprecision(12);
    int t=1;
    //cin>>t;
    while(t--){
        solve();
    }
}

[NOIP2017 普及组] 跳房子

跳房子,也叫跳飞机,是一种世界性的儿童游戏,也是中国民间传统的体育游戏之一。

跳房子的游戏规则如下:

在地面上确定一个起点,然后在起点右侧画 $n$ 个格子,这些格子都在同一条直线上。每个格子内有一个数字(整数),表示到达这个 格子能得到的分数。玩家第一次从起点开始向右跳,跳到起点右侧的一个格子内。第二次再从当前位置继续向右跳,依此类推。规则规定:

玩家每次都必须跳到当前位置右侧的一个格子内。玩家可以在任意时刻结束游戏,获得的分数为曾经到达过的格子中的数字之和。

现在小 R 研发了一款弹跳机器人来参加这个游戏。但是这个机器人有一个非常严重的缺陷,它每次向右弹跳的距离只能为固定的 $d$。小 R 希望改进他的机器人,如果他花 $g$ 个金币改进他的机器人,那么他的机器人灵活性就能增加 $g$,但是需要注意的是,每 次弹跳的距离至少为 $1$。具体而言,当 $g<d$ 时,他的机器人每次可以选择向右弹跳的距离为 $d-g,d-g+1,d-g+2,\ldots,d+g-1,d+g$;否则当 $g \geq d$ 时,他的机器人每次可以选择向右弹跳的距离为 $1,2,3,\ldots,d+g-1,d+g$。

现在小 R 希望获得至少 $k$ 分,请问他至少要花多少金币来改造他的机器人。

若无论如何他都无法获得至少 $k$ 分,输出 $-1$。

样例输入 #1

7 4 10
2 6
5 -3
10 3
11 -3
13 1
17 6
20 2

样例输出 #1

2
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n' 
const int N=2e6;
int n,d,k;
int x[N],s[N];
int dp[N];
deque<int>p;         
bool check(int mid){
    memset(dp,-127,sizeof(dp));
    p.clear();
    int l=max((int)1,d-mid);
    int r=min(d+mid,x[n]);
    int idx=0;
    int num=0;
    dp[0]=0;
    for(int i=1;i<=n;i++){
        if(x[i]>=l&&x[i]<=r){
            idx=i;
            dp[idx]=s[idx];
            num=dp[idx];
            break;
        }
    }
    if(idx==0) return 0;
    for(int i=2;i<=n;i++){
        while(x[idx]<=x[i]-l){
            while(!p.empty()&&dp[idx]>=dp[p.back()]){
                p.pop_back();
            }
            p.push_back(idx);
            idx++;
        }
        while(!p.empty()&&x[p.front()]<x[i]-r) p.pop_front();
        if(!p.empty())
            dp[i]=dp[p.front()]+s[i];
        num=max(num,dp[i]);
    }
    if(num>=k) return 1;
    else return 0;
}
void solve(){
    cin>>n>>d>>k;
    for(int i=1;i<=n;i++){
        cin>>x[i]>>s[i];
    }
    int l=0;
    int r=1e9;
    while(l<r){
        int mid=l+r>>1;
        if(check(mid)) r=mid;
        else l=mid+1;
    }
    if(l>=1e9) l=-1;
    cout<<l<<endl;
}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout << fixed << setprecision(12);
    int t=1;
    while(t--){
        solve();
    }
}

切蛋糕

今天是小 Z 的生日,同学们为他带来了一块蛋糕。这块蛋糕是一个长方体,被用不同色彩分成了 $n$ 个相同的小块,每小块都有对应的幸运值。

小 Z 作为寿星,自然希望吃到的蛋糕的幸运值总和最大,但小 Z 最多又只能吃 $m(m\le n)$ 小块的蛋糕。

请你帮他从这 $n$ 小块中找出连续的 $k(1 \le k\le m)$ 块蛋糕,使得其上的总幸运值最大。

形式化地,在数列 ${p_n}$ 中,找出一个子段 $[l,r](r-l+1\le m)$,最大化 $\sum\limits_{i=l}^rp_i$。

样例输入 #1

5 2
1 2 3 4 5

样例输出 #1

9
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n' 
const int N=2e6;
int a[N],dp[N],s[N];
deque<int>dq1,dq2;
int n,h;
deque<int>p;         
void solve(){
   int n,m;
   cin>>n>>m;
   int ans=0;
   for(int i=1;i<=n;i++){
       cin>>a[i];
       s[i]=s[i-1]+a[i];
    }
    int j=1;
    for(int i=1;i<=n;i++){
        while(j<=min(i+m-1,n)){
            while(!p.empty()&&s[j]>=s[p.back()]){
                p.pop_back();
            }
            p.push_back(j);
            j++;
        }
        if(p.front()<i) p.pop_front();
        ans=max(ans,s[p.front()]-s[i-1]);
    }
    cout<<ans<<endl;
}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout << fixed << setprecision(12);
    int t=1;
    //cin>>t;
    while(t--){
        solve();
    }
}
阅读全文
avatar
joso

齐鲁工业大学(山东省科学院)