[数学] 向量函数的雅可比矩阵与链式法则

复习一下我的数学知识T_T

1. 回顾高等数学:多元数量函数的梯度

回想高等数学中常见的多元数量函数$f:\mathbb{R}^{n}\rightarrow \mathbb{R}^{1}$,我们可以把他的输入当作一个向量 $\bf{x}\in \mathbb{R}^{n}$,输出$y=f(\bf{x})\in \mathbb{R}^{1}$是一个数字。那么由高数的知识我们知道$f$的梯度定义为
$$
\nabla f_{\boldsymbol{x}} \overset{\underset{\mathrm{def}}{}}{=} \left[ \frac{\partial f }{\partial x_1}, \frac{\partial f }{\partial x_2},\cdots,\frac{\partial f }{\partial x_n} \right]=\frac{\partial f }{\partial \boldsymbol{x}}
$$

有了上式,我们还可以写出全微分的向量化表示

\[
\begin{aligned}
df &= \frac{\partial f}{\partial x_1}dx_1+\frac{\partial f}{\partial x_2}dx_2+\cdots+\frac{\partial f}{\partial x_n}dx_n \\
&=\left[ \frac{\partial f }{\partial x_1}, \frac{\partial f }{\partial x_2},\cdots,\frac{\partial f }{\partial x_n} \right] \left[dx_1, dx_2,\cdots,dx_n \right]^T \\
&=\frac{\partial f }{\partial \boldsymbol{x}} d\boldsymbol{x}
\end{aligned}
\]

接下来我们将其推广到向量函数。向量函数的“梯度”其实就是雅可比矩阵
继续阅读“[数学] 向量函数的雅可比矩阵与链式法则”

2019 kali 笔记本电脑 双系统 双显卡 配置nvidia显卡支持外接hdmi显示器

。。。绝大多数人都会照着官方的教程做,但是这个操作是针对只有一张nvidia显卡的台式机来说的,而不是笔记本的双显卡。。。。按照这个教程,装完之后是可以用这个gpu做运算的,但是不能用来做显示。。。插上外接显示器没有任何反应,用xrandr根本检测不到有外界显示器存在。

出现这个问题的原因是官方的配置里缺少了一步配置显示的操作。。。如果只有一张卡的话装系统的时候显示设置就配置好了,双显卡的话默认的显示用的是核显,独显根本没管,所以我们要手动操作一下。

注意:在参考本配置之前请务必先按照官网Doc走一遍,确保gpu驱动已经装好,可以进行运算测试之后再进行。

我的kali版本

Linux 5.3.0-kali2-amd64 #1 SMP Debian 5.3.9-3kali1 (2019-11-20) x86_64 GNU/Linux
  1. 首先安装kernel headers,不然驱动编译可能会出问题
apt install linux-headers-$(uname -r)

继续阅读“2019 kali 笔记本电脑 双系统 双显卡 配置nvidia显卡支持外接hdmi显示器”

[博弈论][组合游戏][组合计数] codeforces 812E Sagheer and Apple Tree

题目链接

题意:

给定一棵树,每个节点上有 $a_i$ 个苹果,两个人轮流操作,每次把一个节点的一部分(或全部)苹果移到他的某个儿子中(如果是叶子节点没有儿子那么就吃掉这些苹果),最后不能走的人输。现在在游戏开始前先手可以选取两个不同的节点交换他们之间的苹果数,问有多少种交换方式可以使得先手必胜。
数据范围: $1\le n \le 10^5,\;1\le a_i \le 10^7$

题解:

博弈本身是很简单的阶梯博弈。由阶梯博弈的知识,求胜负只需要计算奇数位上的SG函数异或和即可,这题只不过把阶梯转移到了树上,但是略微分析就可以发现和链状的阶梯是没有区别的,都是奇数往偶数转移当作拿走,偶数往奇数转移就对称操作。

  • 然后考虑计数:
    • 假设初始状态是必胜态,那么可以先计入奇数层自己内部交换和偶数层自己内部交换的对数,分别为 $C_{num\;of\;odd\;level}^2$ 和 $C_{num\;of\;even\;level}^2$;
    • 然后计算奇偶层之间互换的对数,设异或和结果为 $ans$ ,假设一对数 $a_u$ $a_v$ 交换之后可以使得 $ans=0$ ,那么由亦或的性质可得 $a_v=ans\oplus a_u$,那么我们就可以枚举 $a_u$ 的值,找 $a_v$ 有多少个,而后者利用 $map$ 可以很容易的预处理出来。
      继续阅读“[博弈论][组合游戏][组合计数] codeforces 812E Sagheer and Apple Tree”

[CV][经典算法] Grab Cut C++实现

暑假时候复现的一篇计算机视觉经典抠图算法grabcut的论文,“GrabCut” — Interactive Foreground Extraction using Iterated Graph Cuts

代码存一下,虽然我转行了不搞cv了hhh

myGrabCut.h

#pragma once

#include<graph.cpp>
#include<opencv2/opencv.hpp>
#include <opencv2/core.hpp>
#include <opencv2/core/types.hpp>
#include<string>
typedef Graph<double, double, double> GraphType;

using namespace cv;


class GMM {
public:
	Mat model;
	static const int compo_k = 5;
	double coefs[compo_k];
	double mean[compo_k][3];
	double covs[compo_k][3][3];
	double inv_covs[compo_k][3][3];
	double det_covs[compo_k];
	double sums[compo_k][3];
	double prods[compo_k][3][3];
	int sample_num[compo_k];
	int tot_sample_num;

	GMM();
	void calc_inv_det(int ci);
	double prob_GMM(const Vec3d color) const;
	double prob_Gi(int ci, const Vec3d color) const;
	int sle_compo(const Vec3d color) const;
	void init_param();
	void add_pixel(int ci, const Vec3d color);
	void esti_param();

};


static double calcBeta(const Mat& img);
	
/*
  Calculate weights of noterminal vertices of graph.
  beta and gamma - parameters of GrabCut algorithm.
 */
static void calcNWeights(const Mat& img, Mat& leftW, Mat& upleftW, Mat& upW,
	Mat& uprightW, double beta, double gamma);

/*
  Check size, type and element values of mask matrix.
 */
static void checkMask(const Mat& img, const Mat& mask);

/*
  Initialize mask using rectangular.
*/
static void initMaskWithRect(Mat& mask, Size imgSize, Rect rect);

/*
  Initialize GMM background and foreground models using kmeans algorithm.
*/
static void initGMMs(const Mat& img, const Mat& mask, GMM& bgdGMM, GMM& fgdGMM);

/*
  Assign GMMs components for each pixel.
*/
static void assignGMMsComponents(const Mat& img, const Mat& mask, const GMM& bgdGMM,
	const GMM& fgdGMM, Mat& compIdxs);

//论文中:迭代最小化算法step 2:从每个高斯模型的像素样本集中学习每个高斯模型的参数
/*
  Learn GMMs parameters.
*/
static void learnGMMs(const Mat& img, const Mat& mask, const Mat& compIdxs, GMM& bgdGMM, GMM& fgdGMM);

/*
  Construct GCGraph
*/
static void constructGCGraph(const Mat& img, const Mat& mask, const GMM& bgdGMM, const GMM& fgdGMM, double lambda,
	const Mat& leftW, const Mat& upleftW, const Mat& upW, const Mat& uprightW,
	GraphType*& graph);

/*
  Estimate segmentation using MaxFlow algorithm
*/
static void estimateSegmentation(GraphType* graph, Mat& mask, std::vector<double>& result);

void mygrabCut(InputArray _img, InputOutputArray _mask, Rect rect,
	InputOutputArray _bgdModel, InputOutputArray _fgdModel,
	int iterCount, int mode);//,std::vector<double>& result);

主程序代码,其中最大流最小割直接用了maxflow-v3.01的库,其他调用了一些OpenCv的基本接口我不会说我就是照着OpenCv的库函数写的
继续阅读“[CV][经典算法] Grab Cut C++实现”

[比赛补题][线段树][连通分量][二分图] codeforces gym 102055 B Balance of the Force (CCPC 2018 Final)

题目链接

题意:

给定 $N$ 个人,每个人可以选择加入黑暗 $Dark$ 或者光明 $Light$ 两种阵营,他们加入不同的阵营之后获得的力量值是不同的,即 $D_i$ 和 $L_i$ 。然后有些人之间有矛盾,是不能加入同一阵营的,矛盾的对数共有 $M$ 对,现在给出所有的矛盾和所有的 $L_i D_i$,问在所有可能存在的最终阵营分配情况中,力量值最大的人和力量值最小的人之间的差最小可能是多少?如果说不可能分配的话输出$IMPOSSIBLE$。其中 $1\le N\le 2*10^5$, $0\le M\le 2*10^5$, $1\le L_i D_i \le 10^9$ ,共20组test,时限4S

题解:

一个非常有意思的,看上去像是图论其实是数据结构的题目。。其实以前做过类似的?应该是今年西安邀请赛时候,区别在于那个题目应该是一种背包的性质,一个联通分量要么选,要么不选,而这个题是每个连通分量都得选,但是要从两套孪生方案中选择一种,每一种都会有一个最大值和一个最小值。

把两两不能在一起的人之家连一条无向边,那么一堆互相有关系的人就形成了一个连通分量,不在同一个分量里的人互相是完全没有影响的,无论这个人是选择D还是L都跟我的选择没关系。而在同一个联通分量里的情况的话,可以发现只要有一个人的状态确定了就可以用二分染色的方法确定连通分量里其他所有人的状态。因此我们首先用染色法对每个连通分量判定,如果能染色成功那么就说明有可行解存在,否则输出$IMPOSSIBLE$,另外,在dfs的过程中还要顺便把每个连通分量的两种方案的最大值最小值总共四个数分别求出来(这里我当时就写假了emmm)。之后考虑如何让差值最小化,一般让某个东西最小化很容易想到二分,但是这个题目的条件下,如果要二分就必须再 $O(n)$ 枚举一个下界,乘上 $O(n)$ 的check的话就n方了,显然无法接受。这时候观察这个题目,此时的每个元素(连通分量)正好有两个属性,发现我们可以用一个常用的套路来尝试,那就是按照其中一个属性排序然后对另一个属性套某种log的数据结构。。对这个题来说,回想我们刚才考虑的二分,他必须有一个操作就是枚举出一个下界or上界,那么如果我们就着这个操作进行下去会怎么样?如果我们能一边枚举一边很快的计算出来当当前枚举到的最小值是最小值的时候最大值最小是多少,那么这个问题就可以解决了。
继续阅读“[比赛补题][线段树][连通分量][二分图] codeforces gym 102055 B Balance of the Force (CCPC 2018 Final)”

[DP][子序列][有限状态机][正则表达式] codeforces 1096D Easy Problem

题目链接

题意:

给一个长为1e5的串,问至少去掉多少个字符后该串中不包含hard这个子序列。

题解:

连续两场有类似的这样的题目搞砸了,心情复杂。

这个是比较简单的版本,当时真的怎么就没想到用有限状态机来表示呢。。。还是naive,太菜了。。。

用 dp[i][j] 表示到第 i 个位置为止,使得前缀为j长度的hard的子串不出现至少需要删除多少个字符。状态转移很简单,当为char==h&&j==0的时候dp[i][j]=dp[i-1][j]+1;
其他的就是枚举最后一步决策,按照删当前的还是不删当前的来分就行了。

这题搞砸真的非常难过,非常难过

继续阅读“[DP][子序列][有限状态机][正则表达式] codeforces 1096D Easy Problem”

火柴棒DP 【未完待续】

这种题主要是比较套路,在火柴棒的约束下搞事情

#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>
 
using namespace std;
#define pr(x) cout << #x << " = " << x << "  "
#define prln(x) cout << #x << " = " << x << endl
const int N = 1e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;
 
typedef long long LL;
 
int n, m;
LL dp[505][2][2][2]; //left, carry, place B, place C
int need[] = {6, 2, 5, 5, 4, 5, 6, 3, 7, 6};
 
void add(LL& x, LL y) {
    x += y;
    if(x >= m) x -= m;
}
 
LL dfs(int n, bool carry, bool B, bool C) {
    LL& ret = dp[n][carry][B][C];
    if(~ret) return ret;
    if(n == 0) {
        if(!carry && !B && !C) return ret = 1;
        return ret = 0;
    }
 
    ret = 0;
    if(B && C) {
        for(int i = 0; i < 10; ++i) {
            for(int j = 0; j < 10; ++j) {
                int sum = need[i] + need[j] + need[(i + j + carry) % 10];
                if(sum > n) continue;
                bool nxt = i + j + carry >= 10;  //check next carry
                //always can continue to place B & C
                add(ret, dfs(n - sum, nxt, 1, 1));
                if(i) add(ret, dfs(n - sum, nxt, 0, 1)); //choose not to place B
                if(j) add(ret, dfs(n - sum, nxt, 1, 0)); //choose not to place C
                if(i && j) add(ret, dfs(n - sum, nxt, 0, 0)); //choose neither place
            }
        }
    } else if(B) {
        for(int i = 0; i < 10; ++i) {
            int sum = need[i] + need[(i + carry) % 10];
            if(sum > n) continue;
            bool nxt = i + carry >= 10;
            add(ret, dfs(n - sum, nxt, 1, 0));
            if(i) add(ret, dfs(n - sum, nxt, 0, 0));
        }
    } else if(C) {
        for(int j = 0; j < 10; ++j) {
            int sum = need[j] + need[(j + carry) % 10];
            if(sum > n) continue;
            bool nxt = j + carry >= 10;
            add(ret, dfs(n - sum, nxt, 0, 1));
            if(j) add(ret, dfs(n - sum, nxt, 0, 0));
        }
    } else {
        if(carry && n == need[1]) ret = 1;
    }
    return ret;
}
 
int main() {
#ifdef LOCAL
    freopen("in.txt", "r", stdin);
//  freopen("out.txt","w",stdout);
#endif
    ios_base::sync_with_stdio(0);

    int t; scanf("%d", &t);
    int kase = 0;
    while(t--) {
        scanf("%d%d", &n, &m);
        n -= 3; //subtract - & = signs' cost
        memset(dp, -1, sizeof dp);
        printf("Case #%d: %I64d\n", ++kase, dfs(n, 0, 1, 1));
    }
    return 0;
}

[比赛补题][FFT][组合数学][母函数][×]2019 xdu网络赛F XDOJ 1409: 背包弹夹平底锅

2019 xdu网络赛F XDOJ 1409: 背包弹夹平底锅

题目链接

网络赛最难的一个题,也是我唯一一个不会做的。

看了大大的题解发现我其实已经推到了倒数第二步T_T

但是眼拙没看出来可以写成卷积形式。。。所以按照那个推法无论如何都是O(n^2)的复杂度,变成卷积形式就可以用fft加速直接O(nlogn)一次性求出所有点的值了。。。
继续阅读“[比赛补题][FFT][组合数学][母函数][×]2019 xdu网络赛F XDOJ 1409: 背包弹夹平底锅”

[AC自动机][状态压缩DP][DFS]

题目链接: Password Suspects

这题让我回想起了某现场赛被某大佬支配的恐惧

感觉是dp,而且这个子串的数据量一看就是状压,然后在推转移方程的时候死活推不出来一个不交叉的。可耻的搜了题解,看到大佬们的标题:AC自动机+DP
我就

~~其实是常见的一种操作只是我太久没做过字符串的题目了~~
在思考递推方程的时候就一直在纠结前缀与后缀重合的这种情况怎么表示状态,然后忽然意识到ac自动机(更确切的说是Trie图)就是可以唯一的确定一个匹配状态的数据结构!这种题一般都是给定一些字串,然后给出一些约束条件,比如要求

  • 匹配所有字串,这种就是本题类型的,往往需要状压
  • 不能匹配任一字串,即匹配走过的路径上不能有任何一个危险节点
  • 满足某些条件的字串
  • 等等等等(我还没来得及总结那么多

继续阅读“[AC自动机][状态压缩DP][DFS]”