【备忘录】常用函数

TSY dalaoのblog

icehome 的头像Icehome

字符读入

char C()
{
	char ch=getchar();
	while(!(ch>=32&&ch<=126))ch=getchar();
	return ch;
}

堆上滤/下滤

void UP(int x)
{
	while(x>1&&st[x>>1]>st[x])
	{
		st[x>>1]^=st[x]^=st[x>>1]^=st[x];
		x>>=1;
	}
}
void DOWN(int x)
{
	while(x*2<=n)
	{
		int y=x<<1;
		if(y<n&&st[y+1]<st[y])y++;
		if(st[y]>=st[x])break;
		st[x]^=st[y]^=st[x]^=st[y];
		x=y;
	}
}

归并排序

void msort(int l,int r)
{
	if(l==r)return;
	int mid=(l+r)>>1;
	msort(l,mid);msort(mid+1,r);
	int i=l,j=mid+1,k=l;
	while(i<=mid&&j<=r)
	{
		if(a[i]>a[j])c[k++]=a[i++];
		else c[k++]=a[j++];
	}
	while(i<=mid)c[k++]=a[i++];
	while(j<=r)c[k++]=a[j++];
	for(int t=l;t<=r;t++)a[t]=c[t];
}

最大最小值

int max(int x,int y){return (x>y?x:y);}
int min(int x,int y){return (x<y?x:y);}

绝对值

int abs(int x){return (x<0?-x:x);}

快速幂

int ksm(int x,int y,int mod)
{
	int s=1;
	while(y)
	{
		if(y&1)s=s*x%mod;
		x=x*x%mod;
		y>>=1;
	}
	return s;
}

模意义下的大整数乘法

long long slowmul(long long x,long long y,long long mod)//O(log)
{
	long long s=0;
	while(y)
	{
		if(y%2)s=(s+x)%mod;
		x=x*2%mod;
		y/=2;
	}
	return s;
}

long long fastmul(long long x,long long y,long long mod)//O(1)
{
	unsigned long long s=(unsigned long long)(x*y)-(unsigned)((long double)x/mod*y+0.5L)*mod;
	return (s+mod)%mod;
}

两点间的距离

double XtoY(int stx,int sty,int edx,int edy)
{
	return sqrt((stx-edx)*(stx-edx)+(sty-edy)*(sty-edy));
}

View original post

【单源最短路】Dijkstra算法详解 堆优化

用途: 用于求图中指定两点之间的最短路径,或者是指定一点到其它所有点之间的最短路径。实质上是贪心算法。

基本思想:
1.将图上的初始点看作一个集合S,其它点看作另一个集合
2.根据初始点,求出其它点到初始点的距离d[i] (若相邻,则d[i]为边权值;若不相邻,则d[i]为无限大)
3.选取最小的d[i](记为d[x]),并将此d[i]边对应的点(记为x)加入集合S (实际上,加入集合的这个点的d[x]值就是它到初始点的最短距离)
4.再根据x,更新跟 x 相邻点 y 的d[y]值:d[y] = min{ d[y], d[x] + 边权值w[x][y] },因为可能把距离调小,所以这个更新操作叫做松弛操作。 (仔细想想,为啥只更新跟x相邻点的d[y],而不是更新所有跟集合 s 相邻点的 d 值? 因为第三步只更新并确定了x点到初始点的最短距离,集合内其它点是之前加入的,也经历过第 4 步,所以与 x 没有相邻的点的 d 值是已经更新过的了,不会受到影响)
5.重复3,4两步,直到目标点也加入了集合,此时目标点所对应的d[i]即为最短路径长度。 (注:重复第三步的时候,应该从所有的d[i]中寻找最小值,而不是只从与x点相邻的点中寻找。想想为什么?) 原理:这里不进行严格证明,Dijkstra的大致思想就是,根据初始点,挨个的把离初始点最近的点一个一个找到并加入集合,集合中所有的点的d[i]都是该点到初始点最短路径长度,由于后加入的点是根据集合S中的点为基础拓展的,所以也能找到最短路径。

伪代码:

清除所有点的标号;
设d[0]=0,其他d[i]=INF;//INF是一个很大的值,用来替代正无穷
循环n次 { 
在所有未标号结点中,选出d值最小的结点x;
给结点x标记;
对于从x出发的所有边(x,y),更新d[y] = min{d[y], d[x]+w(x,y)} 
}

实现代码:(C++) 以下算法用于求所有点到初始点的最短距离(保存在d[i]中),n是节点数,m是边的数量。

memset(v, 0, sizeof(v)); 
for(int i = 0; i &lt; n; i++) d[i] = (i==0 ? 0 : INF); 
for(int i = 0; i &lt; n; i++) {  
  int x, m = INF;  
  //3.如果y没有被加入集合,且d[y]是最小的,则把y加入集合且x = y
  for(int y = 0; y &lt; n; y++) 
    if(!v[y] &amp;&amp; d[y] &lt;= m) m = d[y], x = y; 
  v[x] = 1;  //新的点加入集合(这是更新之后的新x)
  //4.更新x相邻的点的d[i],实际上这里更新的是所有点,但是与x未相邻的w[x][y]值是无穷大,不可能被更新
  for(int y = 0; y &lt; n; y++) d[y] = min(d[y], d[x] + w[x][y]); 
}

· 稍微修改一下,便可以把路径存储起来(将当前点y的前一个点x存储在p[y]中),之后就方便递归打印了。

将d[y] = min(d[y], d[x] + w[x][y])改成:

if(d[x] + w[x][y] &lt; d[y]) {
 d[y] = d[x] + w[x][y];
 p[y] = x;

· 类似于d[y] = min(d[y], d[x] + w[x][y])这种操作,它每次都可能把距离更新成更小的值,所以这类操作又叫作”松弛操作“。

优化方法(vector & 邻接表) 上述代码的复杂度为$O(n^2)$,这里可以用邻接表将其优化为$O(m \log n)$,之所以为优化,是因为m往往远小于$n^2$。为了方便,我们把边封装在结构体中:

struct Edge {
  int from, to, dist;
  Edge(int u, int v, int d):from(u),to(v),dist(d) {}
}

` 又因为Dijkstra算法中,每次循环需要提取出最小$d_i$对应的点,所以这里可以用到优先队列 priority_queue,队列内部是根据d值进行排序的,又需要d值对应的点的信息(u),所以这里可以用另一个结构体存储放在优先队列里的元素(d相当于d[i],u相当于i)。

struct DistNode {
  int d, u;
  bool operator  rhs.d; //这样一来,队列中在最顶层的是最小值
  }
}
接下来我们再建立一个统一的邻接表数据结构,用于接收数据并构造邻接表,并能Dijkstra。
struct Dijkstra {
  int n, m;
  vector edges;//相当于数组r[i],用于存储每条边
  vector G[maxn];//邻接表,只需存储边的标号即可
  book done[maxn];//用于判断是否已经处理过该节点
  int d[maxn];距离
  int p[maxn];上一条弧
  //清除垃圾数据,初始化邻接表和deges
  void setn(int n) {
    this-&gt;n = n;
    for(int i = 1; i &lt;= n; i++) G[i].clear();
    edges.clear();
  }
  //添加一条边
  void addedge(int from, int to , int dist) {
    edges.push_back(Edge(from, to, dist));
    m = edges.size();
    G[from].push_back(m - 1);
  }
  //Dijkstra
  void dijkstra(int s) {
  }
}
   · 接下来实现dijkstra函数:

void dijkstra(int s) {
  priority_queue Q;
  Q.push_back(DistNode{0, s});
  for(int i = 1; i &lt;= n; i++) d[i] = INF;//把d[i]都设置为最大值
  d[s] = 0;
  memset(done, 0, sizeof(done));
  while(!Q.empty()) {
    DistNode x = Q.top(); Q.pop();
    int u = x.u;
    if(done[u]) continue;//如果这个点已经被提出过了,直接抛弃(适用于那种松弛之后重复放入队列的点)
    done[u] = true;
    for(int i = 0; i &lt; Q[u].size(); i++) 
      Edge&amp; e = edges(Q[u][i]);
      if(e.dist + d[u] &lt; d[e.to] &amp;&amp; d[u] &lt; INF) {
        d[e.to] = e.dist + d[u];
        p[e.to] = G[u][i];
        Q.push_back(Edge(d[e.to], e.to));//把松弛过后点的d值重新放入队列
      }
    }
  }
}

cmd命令提示符 gdb考场常用指令

前言:我是GD选手,而GD的CSP/NOIP考场环境都是Windows系统+Linux虚拟机,所以写一下常用的指令,考前复习用

cmd常用指令

1.打开cmd

本文以WIN10系统为例

在开始菜单中打开

Step 1:在开始(按钮) 上左键单击 pic


Step 2:鼠标滚轮向下滚动(在"启动"中)pic


Step 3:在Windows系统文件夹,已折叠 (列表项目) 上左键单击(在"启动"中) pic


Step 4:在命令提示符 (列表项目) 上左键单击(在"启动"中) pic

用“启动”打开

Step 1: 在键盘上输入[Win+R] pic


Step 2:在"打开(O): (编辑)"上键盘输入“cmd”(在"运行"中) pic


Step 3: 在"确定 (按钮)"上左键单击(在"运行"中) pic

常用指令大全

1.help指令

操作 结果
help 打开帮助
命令 -help 打开命令帮助
命令 /? 打开命令帮助

2.cd指令

操作 结果
cd \ 跳转到硬盘的根目录
cd C:\WINDOWS 跳转到当前硬盘的其他文件
D: 跳转到其他硬盘
cd /d E:\Software 跳转到其他硬盘的其他文件夹(PS:必须加/d)
cd.. 跳转到上一层目录

3.打开各种程序

操作 结果
calc 打开计算器
mspaint 打开画图
control 打开控制面板
notepad 打开记事本

GDB常用命令

打开gdb

Windows环境

打开cmd,输入gdb或按[Win+R],输入gdb

Linux环境

在终端输入$gdb

GDB命令

命令 命令缩写 命令说明
list l 显示多行源代码
break b 设置断点,程序运行到断点的位置会停下来
info i 描述程序的状态
run r 开始运行程序
display disp 跟踪查看某个变量,每次停下来都显示它的值
step s 执行下一条语句,如果该语句为函数调用,则进入函数执行其中的第一条语句
next n 执行下一条语句,如果该语句为函数调用,不会进入函数内部执行(即不会一步步地调试函数内部语句)
print p 打印内部变量值
continue c 继续程序的运行,直到遇到下一个断点
set var name=v 无 设置变量的值
start st 开始执行程序,在main函数的第一条语句前面停下来
file 无 装入需要调试的程序
kill k 终止正在调试的程序
watch 无 监视变量值的变化
backtrace bt 产看函数调用信息(堆栈)
frame f 查看栈帧
quit q 退出GDB环境

GDKOI2021提高组 溺水记

过于蒟蒻的我。。。

感觉成了炮灰。。。

DAY1

三天中最简单的一天了,但因为我的手残,挂了很多分。。。

割

题目全是使用数学语言描述的,但理解没有问题

题目大意

有一个无向图,将点分成两部分(就像二分图),并且要求两部分之间连的边要是原来的$\frac{1}{2}$以上

思路

因为最终的图要像二分图一样,所以可以考虑删边,将一个点放入一个集合中,并删除与该集合连接的边,显然是加入连的边较多的一个集合更优,而题目保证有解,所以$O(n)$跑一边贪心即可,但考试时打挂了。。。

忙碌的出题人

直接$O(n^2)$硬上,有人用线段树和ST表复杂度反而更大

回文

10%

$O(n^3)$暴力

30%

manacher暴力

100%

manacher预处理+DP

东方永夜抄

Subtask1

暴力枚举

100%

奇奇怪怪的多项式

DAY2

游戏

思路

明显是期望DP

$p_i$指胜率($\frac{x_i}{y_i}$),设$f_i$表示从$i-1$颗星打到$i$期望对局的次数,那么

$$f_i=(f_i+f_{i-1})\times (1-p_i)$$

但方程中仍有$f_i$,所以转化为

$f_i=\frac{f_{i-1}\times (1-p_i)+1}{p_i}$,$f_1=\frac{1}{p_1}$

接着就可以$O(n)$转移了

答案为$\sum_{i=1}^nf_i$.

群岛

线段树乱搞即可

抄写

manacher预处理回文

堆

使用$O(n\log n)$的堆构造方法即可

DAY3

???

GDKOI2021普及组滚粗记

DAY -?

noip成绩出了,炸到飞起,但愿人没事

DAY -3

得知有GDKOI,报了名,准备被爆踩。。。

DAY 0

啥也没复习,感觉爆0有望。。。

DAY 1

地图:懒得找规律,暴力乱搞,60pts

灌水:仍然是暴力乱搞。。。

配对:贪心,乱搞一通

旅行:暴力。。。

出考场发现人均T1AC,T460+,很慌

DAY1结算:60+40+100+20=220,呜呜呜

DAY 2

真就一炸到底咯

初中生数学题:数学乱搞即可

二叉树:嫌麻烦没打。。。

我的世界:读错题了,呜呜呜

矩阵:打了个类似二分图的东西

呜呜呜T4打挂了,给我直接炸0

DAY2结算:100+0+0+0=100

DAY 3

一言难尽。。。

三角形相似:按比例关系比较乱搞即可

樱花再见:找到了贪心思路,但没打

数论:完了我不会,暴力硬上

好序列:???smg

emm。。。T1被卡精度了,直接上天

DAY3结算:40+20+20+30=110

三天总结

怎么说,220+100+110=430菜死了

滚粗滚粗