3295: [Cqoi2011]动态逆序对

Time Limit: 10 Sec  Memory Limit: 128 MB

  人工神经互联网(ANN)
简称神经互联网(NN),能效仿生物神经系统对物体所作出的相互反应,是由拥有适应性简单单元(称为神经元)组成的常见并行互连网络

Description

对此类别A,它的逆序对数定义为满意i<j,且Ai>Aj的数对(i,j)的个数。

给1到n的2个排列,依照某种顺序依次删除m个元素。

你的职责是在历次删除三个因素以前计算整个体系的逆序对数。

1  神经元

Input

输入第叁行包蕴七个整数nm,即初步成分的个数和删除的成分个数。

以下n行每行李包裹涵二个1到n时期的正整数,即起来排列。

以下m行每行1个正整数,依次为每便删除的因素。

1.1  M-P 神经元

  如下图所示,来自别的神经元的信号,$x_1, x_2, … , x_n
$,传递过来作为输入实信号,并由此带权重 ($w_1, w_2, … , w_n$) 的连接
(connection) 继续传递,

  然后神经元的总输入值 $\sum w_i x_i$ 与阈值 $\theta$
作相比较,最终通过激活函数$\,f\,$产生神经元的输出: $y = f\left(\,\sum
\limits_{i=1}^n {w_i x_i} – \theta \right)$

   图片 1

Output 

输出包括m行,依次为除去每种成分此前,逆序对的个数。

一.2  激活函数 (activation function)

  理想中,阶跃函数可看做激活函数,将输入值映射为出口值 “0” 和
“一;实际中,常用 Sigmoid 函数作激活函数,
$f(x)=\,\dfrac{1}{一+e^{-x}}$,如下图所示:

  图片 2

  OpenCV 中央银行使的激活函数是另一种情势,$f(x)=\beta
\,\dfrac{1-e^{-\alpha x}}{1+e^{-\alpha x}}$

  当 α = β = 1
时,$f(x)=\dfrac{一-e^{-x}}{壹+e^{x}}$,该函数把恐怕在较大范围内浮动的输入值,“挤压”
到 (-一, 一) 的输出范围内

    图片 3  

  具体的设置函数如下,param1 –> α,param2 –> β

// 设置激活函数,目前只支持 ANN_MLP::SIGMOID_SYM
virtual void cv::ml::ANN_MLP::setActivationFunction(int type, double param1 = 0, double param2 = 0); 

 

Sample Input

5 4

1 5 3 4 2

5 1 4 2

 

二  神经网络

Sample Output

5
2
2
1
样例解释
(1,5,3,4,2) (1,3,4,2) (3,4,2) (3,2) (3)。

2.1  感知机 (perceptron)

 
感知机由两层神经元组成,输入层选用外界输入非确定性信号,而输出层则是一个M-P 神经元。

 
实际上,感知机可视为三个最简便易行的“神经互连网”,用它可很简单的落实逻辑与、或、非等简易运算。

    图片 4

HINT 

N<=100000
M<=50000

  表示立即没看出来是CDQ,树套树倒是瞄出来了,只是不会写啊。

  今后总的来说照旧很醒目标。

  对于排列中的数,记它被去除的光阴为t(未删减的设为m+一),下标为X,大小为Y。

  那么对于三个数i,来看在它前边被删去的数对它的答案的减小值为多少:

    Σ(Tj<Ti &&
Xj<Xi && Yj>Yi) + Σ(Tj<Ti && Xj>Xi && Yj<Yi)

  做三次CDQ就好了。这种未有重新的算起来真是爽啊。

  做的时候从不草稿纸真TM不爽。

#include    <iostream>
#include    <cstdio>
#include    <cstdlib>
#include    <algorithm>
#include    <vector>
#include    <cstring> 
using namespace std;

const int N = 100010;
struct Data{
    int X,Y,T;long long len;
    bool operator <(const Data &t)const{
        return T<t.T;
    }
}rem[N],f[N],t[N];
int n,m,ban[N],bplace[N],A[N],T[N];
long long Ans,ans[N];

inline int gi()
{
    int x=0,res=1;char ch=getchar();
    while(ch>'9' || ch<'0'){if(ch=='-')res=-res;ch=getchar();}
    while(ch>'/' && ch<':')x=x*10+ch-48,ch=getchar();
    return x*res;
}

inline int lb(int k){return k&-k;}

inline void update(int x){for(;x<=n;x+=lb(x))T[x]++;}

inline int query(int x)
{
    int ans=0;
    for(;x;x-=lb(x))ans+=T[x];
    return ans;
}

inline void clean(int x){for(;x<=n;x+=lb(x))T[x]=0;}

inline void calc()
{
    memset(T,0,sizeof(T));Ans=0;
    for(int i=1;i<=n;++i){
        Ans+=(i-query(A[i])-1);
        update(A[i]);
    }
    memset(T,0,sizeof(T));
}
//在外面保证T有序,CDQ内保证X有序,统计Y值。 
//T:时间 X:位置 Y:权值
//CDQ calc Ti<Tj Xi<Xj Y[i]<Y[j]
inline void CDQ(int l,int r)
{
    if(l==r)return;int mid=(l+r)>>1;
    CDQ(l,mid);CDQ(mid+1,r);
    int x=l,y=mid+1,z=l;
    while(x<=mid && y<=r)
        if(f[x].X<f[y].X)update(f[x].Y),t[z++]=f[x++];
        else f[y].len+=query(f[y].Y),t[z++]=f[y++];
    while(x<=mid)t[z++]=f[x++];
    while(y<=r)f[y].len+=query(f[y].Y),t[z++]=f[y++];
    for(int i=l;i<=mid;++i)clean(f[i].Y);
    for(int i=l;i<=r;++i)f[i]=t[i];
}

int main()
{
    n=gi();m=gi();
    for(int i=1;i<=n;++i)
        A[i]=gi();
    for(int i=1;i<=m;++i)
        bplace[ban[i]=gi()]=i;
    for(int i=1;i<=n;++i){
        rem[i].T=bplace[A[i]];
        rem[i].X=i;
        rem[i].Y=A[i];
        if(!rem[i].T)rem[i].T=m+1;
    }
    sort(rem+1,rem+n+1);calc();
    for(int i=0;i<=m;++i)ans[i]=Ans;
    for(int i=1;i<=n;++i){
        f[i]=rem[i];
        f[i].Y=n-f[i].Y+1;
    }
    reverse(f+1,f+n+1);
    CDQ(1,n);sort(f+1,f+n+1);
    for(long long i=1,s=0;i<=n;++i){
        if(!f[i].T)continue;
        ans[f[i].T]-=s;s+=f[i].len;
    }
    for(int i=1;i<=n;++i){
        f[i]=rem[i];
        f[i].X=n-f[i].X+1;
    }
    reverse(f+1,f+n+1);
    CDQ(1,n);sort(f+1,f+n+1);
    for(long long i=1,s=0;i<=n;++i){
        if(!f[i].T)continue;
        ans[f[i].T]-=s;s+=f[i].len;
    }
    for(int i=1;i<=m;++i)
        printf("%lld\n",ans[i]);
    return 0;
}

  

  然后看见小编在上古时期的分块??!!

  1脸懵逼。

  瞪了长时间意识 不是自身写的。

  大约思想就是分块,每块内排序。

  然后查询:

  不在自个儿块里的,五成下。

  在团结块里的,暴力搞搞。

  然后删掉。

  6000ms…过去了…

  昂波莉污波。

#include <cstdio>  
#include <cmath>  
#include <cstdlib>  
#include <algorithm>  
#include <cstring>  
using namespace std;  
#define ll long long  
const int N=200050;  
int n,m,a[N];  
int tmp[N],t[N];//merge-sort  
int belong[N],L[N],R[N],cnt,k,x,y;  
int ad[N];      //i在原数列中的位置为ad[i]    
int b[N];       //每个块维护有序数列   
int sum[N];     //当前块中删除了多少个数   
int adx;  
int i;  
bool f[N];  
ll tot;        //当前逆序对数               开 long long !   
inline int get(){  
  int p=0;char x=getchar();  
  while (x<'0' || x>'9') x=getchar();  
  while (x>='0' && x<='9') p=p*10+x-'0',x=getchar();  
  return p;  
}  
inline void merge_sort(int l,int r){  
  int mid,p,i,j;  
  mid=(l+r)>>1;  
  i=p=l;j=mid+1;  
  while (i<=mid && j<=r)  
    if (tmp[i]>tmp[j]) t[p++]=tmp[j++],tot+=mid-i+1;  
    else t[p++]=tmp[i++];  
  while (i<=mid) t[p++]=tmp[i++];  
  while (j<=r) t[p++]=tmp[j++];  
  for (i=l;i<=r;i++) tmp[i]=t[i];  
  return ;  
}  
inline void merge(int l,int r){  
  if (l>=r) return ;  
  int mid=(l+r)>>1;  
  merge(l,mid);  
  merge(mid+1,r);  
  merge_sort(l,r);  
  return ;  
}  
void init(){  
  n=get();m=get();  
  k=sqrt(n);      //块大小   
  cnt=n/k;if (n%k) cnt++; //块个数   
  for (int i=1;i<=n;i++)  
    a[i]=get(),ad[a[i]]=i,belong[i]=(i-1)/k+1;  
  for (int i=1;i<=cnt;i++)  
    L[i]=i*k-k+1,R[i]=i*k;  
  R[cnt]=n;  
  memcpy(tmp,a,sizeof tmp); 
  tot=0;  
  merge(1,n);  
  memcpy(b,a,sizeof a);  
  for (int i=1;i<=cnt;i++)  
    sort(b+L[i],b+R[i]+1);  
  memset(f,1,sizeof f);  
  return ;  
}  
inline int search(int t,int p){  
  int l,r,ret;  
  l=L[t]; r=R[t]; ret=R[t];  
  while (l<=r){  
    int mid=(l+r)>>1;  
    if (b[mid]<=p) ret=mid,l=mid+1;  
    else r=mid-1;  
  }  
  if (b[ret]>p) ret=L[i]-1;  
  return ret;  
}  
int main(){
  init();  
  for (int p=1;p<=m;p++){  
    printf("%lld\n",tot);  
    y=get();x=ad[y];        //得到在a数列中的位置 所处的块的编号肯定不变  
    k=belong[x];            //x属于第k个块     
    for (i=1;i<k;i++)  
      tot-=R[i]-search(i,a[x]);  
    for (i=k+1;i<=cnt;i++)  
      tot-=search(i,a[x])-L[i]+1-sum[i];  
    for (i=L[k];i<x;i++)  
      if (f[a[i]] && a[i]>a[x]) tot--;  
    for (i=x+1;i<=R[k];i++)  
      if (f[a[i]] && a[i]<a[x]) tot--;  
    adx=search(k,a[x]);  
    b[adx]=0;sum[k]++;  
    f[a[x]]=0;  
    sort(b+L[k],b+R[k]+1);  
  }  
  return 0;  
}

  

2.二 层级结构

 
常见的神经网络,可分为三层:输入层、隐含层、输出层。输入层接收外界输入,隐层和输出层负责对时限信号举行加工,输出层输出最后的结果。

 
以下图为例:每层神经元与下一层神经元全互连,而同层神经元之间不一而再,也不存在跨层连接,那样的组织称为“多层前馈神经网络”(multi-layer
feedforward neural networks)

    图片 5

2.叁  层数设置

   OpenCV 中,设置神经互连网层数和神经元个数的函数为
setLayerSizes(InputArray _layer_sizes),则上海体育场所对应的 InputArray
可由如下代码来组合

// (a) 3层,输入层神经元个数为 4,隐层的为 6,输出层的为 4
Mat layers_size = (Mat_<int>(1,3) << 4,6,4);

// (b) 4层,输入层神经元个数为 4,第一个隐层的为 6,第二个隐层的为 5,输出层的为 4
Mat layers_size = (Mat_<int>(1,4) << 4,6,5,4);

  
如何设置隐层神经元的个数仍是个未决的题材,实际中多应用“试错法”来调整

 

3  OpenCV 函数

1)  创建

static Ptr<ANN_MLP> cv::ml::ANN_MLP::create();  // 创建空模型

二) 设置参数

// 设置神经网络的层数和神经元数量
virtual void cv::ml::ANN_MLP::setLayerSizes(InputArray _layer_sizes);

// 设置激活函数,目前只支持 ANN_MLP::SIGMOID_SYM
virtual void cv::ml::ANN_MLP::setActivationFunction(int type, double param1 = 0, double param2 = 0); 

// 设置训练方法,默认为 ANN_MLP::RPROP,较常用的是 ANN_MLP::BACKPROP
// 若设为 ANN_MLP::BACKPROP,则 param1 对应 setBackpropWeightScale()中的参数,param2 对应 setBackpropMomentumScale() 中的参数
virtual void cv::ml::ANN_MLP::setTrainMethod(int method, double param1 = 0, double param2 = 0);
virtual void cv::ml::ANN_MLP::setBackpropWeightScale(double val); // 默认值为 0.1
virtual void cv::ml::ANN_MLP::setBackpropMomentumScale(double val); // 默认值为 0.1

// 设置迭代终止准则,默认为 TermCriteria(TermCriteria::MAX_ITER + TermCriteria::EPS, 1000, 0.01)
virtual void cv::ml::ANN_MLP::setTermCriteria(TermCriteria val);

3)  训练

// samples - 训练样本; layout - 训练样本为 “行样本” ROW_SAMPLE 或 “列样本” COL_SAMPLE; response - 对应样本数据的分类结果
virtual bool cv::ml::StatModel::train(InputArray samples,int layout,InputArray responses);  

4)  预测

// samples,输入的样本书数据;results,输出矩阵,默认不输出;flags,标识,默认为 0
virtual float cv::ml::StatModel::predict(InputArray samples, OutputArray results=noArray(),int flags=0) const;       

 

肆 代码示例

  上面是 OpenCV 三.三 中,在“扶助向量机”的例程上做的修改,使用 BP
神经网络,达成了和 SVM 相同的归类效果。

   OpenCV 中的 帮忙向量机 (Support Vector Machine),可参见另一篇博文
OpenCV 之 扶助向量机
(壹)

 1 #include "opencv2/core/core.hpp"
 2 #include "opencv2/imgproc/imgproc.hpp"
 3 #include "opencv2/imgcodecs/imgcodecs.hpp"
 4 #include "opencv2/highgui/highgui.hpp"
 5 #include "opencv2/ml/ml.hpp"
 6 
 7 using namespace cv;
 8 
 9 int main()
10 {
11     // 512 x 512 零矩阵
12     int width = 512, height = 512;
13     Mat img = Mat::zeros(height, width, CV_8UC3);
14 
15     // 训练样本
16     float train_data[6][2] = { { 500, 60 },{ 245, 40 },{ 480, 250 },{ 160, 380 },{400, 25},{55, 400} };
17     float labels[6] = {0,0,0,1,0,1};  // 每个样本数据对应的输出
18     Mat train_data_mat(6, 2, CV_32FC1, train_data);
19     Mat labels_mat(6, 1, CV_32FC1, labels);
20 
21     // BP 模型创建和参数设置
22     Ptr<ml::ANN_MLP> bp = ml::ANN_MLP::create();
23 
24     Mat layers_size = (Mat_<int>(1,3) << 2,6,1); // 2维点,1维输出
25     bp->setLayerSizes(layers_size);
26 
27     bp->setTrainMethod(ml::ANN_MLP::BACKPROP,0.1,0.1);
28     bp->setActivationFunction(ml::ANN_MLP::SIGMOID_SYM);
29     bp->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 10000, /*FLT_EPSILON*/1e-6));
30 
31     // 保存训练好的神经网络参数
32     bool trained = bp->train(train_data_mat,ml::ROW_SAMPLE,labels_mat);
33     if (trained) {
34         bp->save("bp_param");
35     }
36 
37     // 创建训练好的神经网络
38 //    Ptr<ml::ANN_MLP> bp = ml::ANN_MLP::load("bp_param");
39 
40     // 显示分类的结果
41     Vec3b green(0, 255, 0), blue(255, 0, 0);
42     for (auto i=0; i<img.rows; ++i) {
43         for (auto j=0; j<img.cols; ++j) {
44             Mat sample_mat = (Mat_<float>(1, 2) << j, i);
45             Mat response_mat;
46             bp->predict(sample_mat,response_mat);
47             float response = response_mat.ptr<float>(0)[0];
48             if (response > 0.5) {
49                 img.at<Vec3b>(i, j) = green;
50             } else if (response < 0.5) {
51                 img.at<Vec3b>(i, j) = blue;
52             }
53         }
54     }
55 
56     // 画出训练样本数据
57     int thickness = -1;
58     int lineType = 8;
59     circle(img, Point(500, 60), 5, Scalar(255, 255, 255), thickness, lineType);
60     circle(img, Point(245, 40), 5, Scalar(255, 255, 255), thickness, lineType);
61     circle(img, Point(480, 250), 5, Scalar(255, 255, 255), thickness, lineType);
62     circle(img, Point(160, 380), 5, Scalar(0, 0, 255), thickness, lineType);
63     circle(img, Point(400, 25), 5, Scalar(255, 255, 255), thickness, lineType);
64     circle(img, Point(55, 400), 5, Scalar(0, 0, 255), thickness, lineType);
65 
66     imwrite("result.png", img);        // 保存训练的结果
67     imshow("BP Simple Example", img);
68 
69     waitKey(0);
70 }

   运维结果如下所示:

  图片 6 

留神:OpenCV 三.0
以上版本,相较在此以前的本子,当中关于机器学习的一对做了较大改观,本身也是踩了1些坑才取得预期的效率。

 1)  代码 #25,必须在 setActivationFunction()
以前,不然磨炼后的结果多为 nan

 2)  代码 #46,response_mat
为预测的结果。若输出向量为 一 列,则如 #四7所示,可径直取出预测结果;若输出向量为 n 列,则可取平均值可能最大值。

      同时,依据平均值或最大值,代码 #4八 处的阈值也要对应的变更。

    float response = 0;
    for (auto i=0;i<n;++i) {
          response += response_mat.ptr<float>(0)[i];
    }

 3)  代码 #39,若已经练习好神经网络的参数,并将其保存到文件
bp_param 中。

      则可将 #22 ~ #35 全体诠释掉,再反注释掉
#3捌,那样,直接加载演练好的神经互联网,便得以利用了。

 

参考资料

  <机器学习> 周志华  第6章

  <统计学习方法> 李航  第2章

  OpenCV 3.0  Tutorials  — Neural Networks

 
OpenCV进阶之路:神经互联网识别车牌字符 
    ☆Ronny丶

 【情势识别】OpenCV中运用神经网络CvANN_MLP 
     xiaowei_cqu