停止!

思考,

回顾。

疑惑?

遗忘…

一直只是在匆忙的赶进度,实际上的确是一点也不扎实。

T1,裸的偏序,想了一个多小时什么也没想到,只打了$O(n^2)$

难道之前学的就这么白学了?

T2,简单dp,没有任何优化就扔了。

单调性优化做了无数次还是不会。

T3,暴力和部分分,部分分写错,暴力没取模。

90多场了啊,这些毛病还在犯,那么考试还有什么意义啊?

需要真正的反思与总结了,需要真正内化成自己的东西,才不是浑浑噩噩度日吧。

少说点废话吧。

T1:序列

求区间大于0,其实就是前缀和之后求$b[j] \geq b[i]$且$a[j] \geq a[i]$的最大$j-i$

看起来向一个三维偏序,但是题目保证有大于0的解,所以当$i \geq j$时并不会更新答案。

所以其实$i/j$这一维并不需要偏序,只用考虑$a/b$两个数组即可。

而$i/j$只是转移值,现在转化为二维偏序问题。

乍一下想动用各种数据结构,但是内存都开不下。

而偏序问题的一个技巧就是通过排序某一维来取消这一维的限制。所以维数-1。

然后就是一个一维偏序问题了,树状数组维护即可。

总的来说就是按$a$排序,将$b$离散化,树状数组维护$i/j$下标最小值即可。

  1. #include<cstdio>
  2. #include<algorithm>
  3. #include<map>
  4. using namespace std;
  5. #define ll long long
  6. map<ll,int>M;
  7. struct P{
  8. ll a;int b,p;
  9. friend bool operator<(P x,P y){
  10. return x.a<y.a||(x.a==y.a&&x.p<y.p);
  11. }
  12. }p[];
  13. long long a[],b[];int n,ans,cnt,t[];
  14. int read(){
  15. register int p=,nt=;register char ch=getchar();
  16. while(ch<''||ch>'')nt=ch=='-',ch=getchar();
  17. while(ch>=''&&ch<='')p=(p<<)+(p<<)+ch-'',ch=getchar();
  18. return nt?-p:p;
  19. }
  20. void set(int p,int w){for(;p<=;p+=p&-p)t[p]=min(t[p],w);}
  21. int ask(int p,int w=){for(;p;p^=p&-p)w=min(w,t[p]);return w;}
  22. int main(){//freopen("sequence.in","r",stdin);
  23. n=read();M[];
  24. for(int i=;i<=n;++i)a[i]=a[i-]+read();
  25. for(int i=;i<=n;++i)b[i]=b[i-]+read(),M[b[i]];
  26. for(map<ll,int>::iterator it=M.begin();it!=M.end();it++)(*it).second=++cnt;
  27. for(int i=;i<=n;++i)p[i]=(P){a[i],M[b[i]],i};
  28. sort(p,p++n);
  29. for(int i=;i<=;++i)t[i]=;
  30. for(int i=;i<=n;++i)ans=max(ans,p[i].p-ask(p[i].b)),set(p[i].b,p[i].p);
  31. printf("%d\n",ans);
  32. }

T2:二叉搜索树

简单的$dp$就是$dp[i][j]$表示区间$[i,j]$之间的点形成树的代价,转移很简单:

$dp[i][j]=min(dp[i][k-1]+dp[k][j]+x[j]-x[i-1])$

其中x是权值的前缀和。

考虑优化:当你在某一个区间右端添加一个点的时候,你最后选定的根节点一定不会左移。

同理,你在左段添加一个点的话,最后选定的最优根节点也一定不会左移。

设$rt[i][j]$表示区间$[i,j]$的最优根节点。那么有

$rt[i][j-1] \leq rt[i][j] \leq rt[i+1][j]$

所以就可以dp了,用上面这个限制一下左右端点。

因为是单调的,所以在$n$种长度里每个位置平均只会被扫$O(n)$次。

所以总的复杂度是$O(n^2)$

  1. #include<cstdio>
  2. int n;long long x[],dp[][],rt[][];
  3. main(){
  4. scanf("%d",&n);
  5. for(int i=;i<=n;++i)scanf("%lld",&x[i]),x[i]+=x[i-];
  6. for(int l=;l<=n;++l)for(int r=l;r<=n;++r)dp[l][r]=;
  7. for(int l=;l<=n;++l)dp[l][l]=x[l]-x[l-],rt[l][l]=l;
  8. for(int L=;L<n;++L)for(int l=,r=l+L;r<=n;++l,++r)
  9. for(int m=rt[l][l+L-];m<=rt[r-L+][r];++m)
  10. if(dp[l][m-]+dp[m+][r]+x[r]-x[l-]<dp[l][r])
  11. dp[l][r]=dp[l][m-]+dp[m+][r]+x[r]-x[l-],rt[l][r]=m;
  12. printf("%lld\n",dp[][n]);
  13. }

T3:走路

题面差评。非得让我栽$n-1$个跟头干啥???做个题结果就真栽跟头了。

挺神仙的一道题。这种分治思想很巧妙。

之前有一道题好像叫《$Dash \ Speed$》是线段树结构的分治,这个也差不多。。。

我们考虑暴力做法。

因为我对期望的理解直到今天$LNC$好好地给我来了一遍才深刻一点。

因为正着推的时候它的转移概率之和不一定为1,而期望的实际含义则是加权平均数。

所以正着推的话要想得到期望就还得一直带着概率算,在出环的图里不可做。

所以要倒着推:设$f_i$表示期望再经过几步才能到$k$点。

转移就是$f_i=\sum\limits_{i \to j}\frac{f_j}{degree_i}$

$degree_i$表是i的出度。

这样的话转移的总概率就是每一条出边累加$\frac{1}{degree_i}$那么总概率当然是1。

而正着的式子却不是,不再赘述。

暴力的思路就是拆掉每个点的所有出边,然后做一次瓜丝消元$f_1$就是解。

  1. #include<cstdio>
  2. #include<algorithm>
  3. using namespace std;
  4. #define mod 998244353
  5. #define ll long long
  6. int n,m,cnt[][],deg[],spj1=,spj2=;ll x[][],inv[],escape[];
  7. ll pow(ll b,int t=mod-,ll a=){for(;t;t>>=,b=b*b%mod)if(t&)a=a*b%mod;return a;}
  8. void Gauss(){
  9. for(int i=;i<=n;++i){
  10. if(!x[i][i])for(int j=i+;j<=n;++j)if(x[j][i])swap(x[i],x[j]);
  11. int inv=pow(x[i][i]);
  12. for(int j=i;j<=n+;++j)x[i][j]=x[i][j]*inv%mod;
  13. for(int j=i+;j<=n;++j)for(int k=n+;k>=i;--k)x[j][k]=(x[j][k]-x[i][k]*x[j][i]%mod+mod)%mod;
  14. }
  15. for(int i=n;i;--i)for(int j=i-;j;--j)x[j][n+]=(x[j][n+]-x[j][i]*x[i][n+]%mod+mod)%mod;
  16. }
  17. main(){
  18. scanf("%d%d",&n,&m);
  19. for(int i=,X,Y;i<=m;++i){
  20. scanf("%d%d",&X,&Y),deg[X]++,cnt[X][Y]++;
  21. if(i!=m&&X>Y)spj1=;
  22. if(X!=Y&&X!=&&Y!=)spj2=;
  23. }
  24. for(int i=;i<=m;++i)inv[i]=pow(i);
  25. for(int A=;A<=n;++A){
  26. for(int i=;i<=n;++i)for(int j=;j<=n+;++j)x[i][j]=;
  27. for(int i=;i<=n;++i)if(A!=i)for(int j=;j<=n;++j)x[j][i]=cnt[i][j]*inv[deg[i]]%mod;
  28. for(int i=;i<=n;++i)x[i][i]--;x[][n+]=-;
  29. //for(int i=1;i<=n;++i,puts(""))for(int j=1;j<=n+1;++j)printf("%lld ",x[i][j]);
  30. Gauss();for(int i=;i<=n;++i)if(i!=A)x[A][n+]+=x[i][n+];
  31. printf("%lld\n",x[A][n+]%mod-);
  32. }
  33. }

不难发现,我们再每一次重造矩阵的时候,其实只有一行是不一样的,就是你单独考虑的$k$那一行。

考虑这个问题:我在瓜丝消元的时候填了一行,然后这一行一直在被消来消去,但是我们始终不用这一行来消其它的行。

那么最后求出的解当然是不会变的对吧?

所以我们先不拆任何边,这样建出一个矩阵。

然后我们对于目前要处理出的k,把这一行看成我们乱加的那一行,这行怎么被消都没有关系,但是我们不能用这一行去消其它行。

这样的话就相当与我们没有加这一堆边,它们对答案没有影响。

考虑递归解决,$solve(l,r)$表示我们正在处理$[l,r]$之内的k。

那么如果k在$[l,mid]$里面,那么$[mid+1,r]$里面的所有边都要生效,就是这些行都要被当作主元来给其它行消元。

消完之后我们就可以递归求解$solve(l,mid)$了。

这样的话如果我们可以发现,$solve(l,r)$的时候其实除了$[l,r]$以外的区间都被当成主元消过了。

那么如果我们递归到了某一个叶节点,那么就是除了这一行以外所有的行都被消过了。

这就和我上面说的情况一致了,这样就可以解出当前点的答案了。

当然如果我们要求解$solve(mid+1,r)$时,我们就需要用$[l,mid]$的边消元。

在这之前我们首先需要把矩阵还原,开数组存一下就好了。

为了方便,我们在每一次消元的时候,并不是把它消成上三角矩阵,而是消成单位矩阵,这样在后面方程比较好解,不用回代。

要注意:如果你的代码递归处函数出现了2个$n$级别的循环而不是$[l,r]$的循环,那么你的复杂度就是$O(n^3 \ log \ n)$了。并不是正解。

而如果只有一个$n$级别的循环,那么你的复杂度就是$O(n^3)$

证明稍后写吧。

问题就在于你的迭代式是下面的哪个:

$T(n)=Nn^2+2T(\frac{n}{2})$

$T(n)=N^2n+2T(\frac{n}{2})$

其中N是指题目输入的N,而n是指当前递归区间的大小。

当然在计算复杂度的时候可以把$N$直接提出,那么第一个式子就是:

$T(n)=N \times t(n) $

$t(n)=n^2+2t(\frac{n}{2})$

根据主定理,得到$t(n)=n^2$

所以$T(n)=n^3$

而对于第二个式子,其复杂度为:

$T(n)=N^2 \times t(n)$

$t(n)=n+2t(\frac{n}{2})$

这是一个非常常见的分治复杂度了。根据常识或者根据主定理,$t(n)=n\  log \ n $

所以$T(n)=n^3 \  log \ n$

  1. #include<cstdio>
  2. #include<algorithm>
  3. using namespace std;
  4. #define mod 998244353
  5. #define int long long
  6. int n,m,cnt[][],deg[],x[][],inv[],ans[];
  7. int pow(int b,int t=mod-,int a=){for(;t;t>>=,b=b*b%mod)if(t&)a=a*b%mod;return a;}
  8. void Gauss(int l,int r,int L,int R){
  9. for(int i=L;i<=R;++i){
  10. int inv=pow(x[i][i]);
  11. for(int j=l;j<=r;++j)x[i][j]=x[i][j]*inv%mod;x[i][]=x[i][]*inv%mod;
  12. for(int j=;j<=n;++j)if(i!=j){
  13. int t=x[j][i];
  14. for(int k=l;k<=r;++k)x[j][k]=(x[j][k]-x[i][k]*t%mod+mod)%mod;
  15. x[j][]=(x[j][]-x[i][]*t%mod+mod)%mod;
  16. }
  17. }
  18. }
  19. void solve(int l,int r){
  20. if(l==r)return ans[l]=(mod+x[][])%mod,(void);
  21. int m=l+r>>;int re[][];
  22. for(int i=;i<=n;++i){for(int j=l;j<=r;++j)re[i][j]=x[i][j];re[i][]=x[i][];}
  23. Gauss(l,r,l,m);solve(m+,r);
  24. for(int i=;i<=n;++i){for(int j=l;j<=r;++j)x[i][j]=re[i][j];x[i][]=re[i][];}
  25. Gauss(l,r,m+,r);solve(l,m);
  26. for(int i=;i<=n;++i){for(int j=l;j<=r;++j)x[i][j]=re[i][j];x[i][]=re[i][];}
  27. }
  28. main(){
  29. scanf("%lld%lld",&n,&m);
  30. for(int i=,X,Y;i<=m;++i)scanf("%lld%lld",&X,&Y),deg[X]++,cnt[X][Y]++;
  31. for(int i=;i<=m;++i)inv[i]=pow(i);
  32. for(int i=;i<=n;++i)for(int j=;j<=n;++j)x[i][j]=cnt[i][j]*inv[deg[i]]%mod;
  33. for(int i=;i<=n;++i)x[i][i]--,x[i][]=-;
  34. solve(,n);for(int i=;i<=n;++i)printf("%lld\n",ans[i]);
  35. }

[考试反思]1029csp-s模拟测试93:殇逝的更多相关文章

  1. [考试反思]0718 NOIP模拟测试5

    最后一个是我...rank#11 rank#1和rank#2被外校大佬包揽了. 啊...考的太烂说话底气不足... 我考场上在干些什么啊!!! 20分钟“切”掉T2,又27分钟“切”掉T1 切什么切, ...

  2. csp-s模拟测试93

    csp-s模拟测试93 自闭场. $T1$想到$CDQ$,因为复杂度少看见一个$0$打了半年还用了$sort$直接废掉,$T2$,$T3$直接自闭暴力分都没有.考场太慌了,心态不好. 02:07:34 ...

  3. 2019.10.29 csp-s模拟测试93 反思总结

    T1: 求出前缀和,三维偏序O(nlog2n)CDQ 二维其实就可以 #include<iostream> #include<cstdio> #include<cstri ...

  4. [考试反思]0814NOIP模拟测试21

    前两名是外校的240.220.kx和skyh拿到了190的[暴力打满]的好成绩. 我第5是170分,然而160分就是第19了. 在前一晚上刚刚爆炸完毕后,心态格外平稳. 想想前一天晚上的挣扎: 啊啊啊 ...

  5. [考试反思]1109csp-s模拟测试106:撞词

    (撞哈希了用了模拟测试28的词,所以这次就叫撞词吧) 蓝色的0... 蓝色的0... 都该联赛了还能CE呢... 考试结束前15分钟左右,期望得分300 然后对拍发现T2伪了写了一个能拿90分的垃圾随 ...

  6. [考试反思]0909csp-s模拟测试41:反典

    说在前面:我是反面典型!!!不要学我!!! 说在前面:向rank1某脸学习,不管是什么题都在考试反思后面稍微写一下题解. 这次是真的真的运气好... 这次知识点上还可以,但是答题策略出了问题... 幸 ...

  7. [考试反思]0801NOIP模拟测试11

    8月开门红. 放假回来果然像是神志不清一样. 但还是要接受这个事实. 嗯,说好听点,并列rank#7. 说难听点,垃圾rank#18. 都不用粘人名就知道我是哪一个吧... 因为图片不能太长,所以就不 ...

  8. [考试反思]0729NOIP模拟测试10

    安度因:哇哦. 安度因:谢谢你. 第三个rank1不知为什么就来了.迷之二连?也不知道哪里来的rp 连续两次考试数学都占了比较大的比重,所以我非常幸运的得以发挥我的优势(也许是优势吧,反正数学里基本没 ...

  9. [考试反思]0714/0716,NOIP模拟测试3/4

    这几天时间比较紧啊(其实只是我效率有点低我在考虑要不要坐到后面去吹空调) 但是不管怎么说,考试反思还是要写的吧. 第三次考试反思没写总感觉缺了点什么,但是题都刷不完... 一进图论看他们刷题好快啊为什 ...

随机推荐

  1. [Android Studio] 2019年Android Studio配置指北

    Android Studio是我学习Android开发路上的第一块绊脚石,新建一个项目,一行代码没动,直接编译不起来,我太难了,所以本文叫指北 本文讲解在9102年如何在国内网络不通畅的情况下流畅的使 ...

  2. IDEA 学习笔记之 Scala项目开发

    Scala项目开发: 由于直接下载Scala plugin太慢,老是中断,所以手动下载: https://plugins.jetbrains.com/ 手动安装Scala plugin: 新建Scal ...

  3. Curl的移植编译以及注意事项

    最近需要用curl来发送http请求,遇到了不少问题,查了不少资料,都是零零散散的,现在总结下.   1.移植编译 ./configure --prefix=$(PWD)/build --host=a ...

  4. 软件开发工具(第9章:使用Eclipse进行C/C++开发)

    一.安装MinGW MinGW是指用来生成可执行文件的编译环境,它是开发C/C++项目 的工具集.为了能够使用Eclipse CDT编译且运行C和C++程序,必须 要安装一个C/C++编译器. 下载: ...

  5. Python高阶函数及函数柯里化

    1 Python高阶函数 接收函数为参数,或者把函数作为结果返回的函数为高阶函数. 1.1 自定义sort函数 要求:仿照内建函数sorted,自行实现一个sort函数.内建函数sorted函数是返回 ...

  6. python-Flask模版注入攻击SSTI(python沙盒逃逸)

    一篇以python Flask 模版渲染为例子的SSTI注入教学~ 0x01 Flask使用和渲染 这里简化了flask使用和渲染的教程 只把在安全中我们需要关注的部分写出来 来一段最简单的FLASK ...

  7. PHP key_exists

    此函数同array_key_exsits(). 1.函数的作用:判断一个数组是否含有某个键值 2.函数的参数: @param string  $key @param array $haystack 3 ...

  8. js转换页面为图片并下载

    <div style="background:red;width: 600px;height: 600px;" class="test"> < ...

  9. 基础安全术语科普(三)——RAT

    什么是RAT? RAT 即 Remote Access Tools (远程管理工具或远程访问工具)的缩写.通俗点说就是木马病毒. RAT 分为两部分——客户端 与 服务端. RAT的工作原理? 服务端 ...

  10. 判断一字串String中是否包含某一串字符串

    String ostype = data.getString("osType").toUpperCase(); //转换为大写 if (ostype.contains(" ...