• [考试反思]0507省选模拟90:信任


    $STL$这种东西你真是不用还不行用了出锅了你还没话说。。。

    $T1$想到正解的一半(而且也写了一半)突然发现不对劲就扔了。

    $T2$是个构造。$yy$了半小时虽然不会严谨证但是大概是对的。

    写完之后发现过不了样例就懵了,发现少考虑一种边界情况。

    结果判这个边界用了好久好久。

    并没有给$T3$时间然后我就结束了。

    结果$T2$莫名$RE$。把$sort$改成$stable sort$就行了。到现在还不明白原理。

    反正以后尽量用$stable sort$吧。

    T1:洛希极限

    大意:网格图有$q$矩形,可以从任意点出发任意点结束,每次可以从$(x_1,y_1)$跳到$(x_2,y_2)$当且仅当$x_1<x_2,y_1<y_2$且两个点同时存在于某一个矩形中。

    求最多跳几步以及跳这么多步的方案数。多测$T le 10^5,n,m le 2 imes 10^3,sum q le 5 imes 10^5$

    设$dp[i][j]$表示最后一次跳到$(i,j)$的最多步数及方案数。枚举点枚举矩形$O(n^2m^2q)$。

    发现最优决策下每一步中,都至少有一个维度的变化量恰好为$1$。

    所以我们只要预处理出每个点能从上一行/列的最左/上端那个位置开始转移,然后枚举就好了。$O(qnm+nm^2)$

    复杂度变成了两部分。后面这个比较要命(是吗。。?)先来优化这个:

    可以发现当你枚举的点右移一个时,转移的左端点不可能左移。也就是说具有单调性,可以单调队列优化。

    但是比较麻烦的一点在于,最大值很好统计,但是你还要方案数,也就是说队列里可能堆着很多最大值一样的点。

    然后你每次查询的是与最大值相等的点的方案数的和,偶尔还要弹掉队首的一个点。

    其实只需要额外维护一个桶来存每种权值的方案数,出入队的时候更新一下桶就行了。

    维护了$n+m$个桶和队列,但并不是更新完一个点的权值就加入,而是用到的时候再插入。我思维僵化了。

    这样$dp$部分的复杂度下降至线性。总复杂度$O(qn+nm)$。理论很大但是$cbx$实测可以$AC$

    (自然也用到了下边这种思想,只不过暴力标记了每一行而已)

    考虑优化前半部分。可以拆分成若干这样的问题:对于第$[u,d]$行,每行的$[l,r]$区间对$l$取$min$

    我思维僵化,最开始的想法是从上到下($u ightarrow d$方向)扫描线,$u$时插入$d+1$时删除。然而发现自己并不会删除。

    但是由于这道题的特殊性,如果我们从右到左($r ightarrow l$方向)做扫描线,发现就不需要删除了。

    因为可以发现对于$i le l$的点,你说更新它们的左端点是$l$,显然是没有意义的。反正最后都可以对$i$取$min$

    所以就直接做区间取$min$的线段树,每行列扫到的时候遍历一下线段树是$O(n)$的。

    所以这一部分的总复杂度就是$O(nm+q log n)$了。总复杂度也就对了。然而我跑不过$cbx$

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 const int mod=1e9+7,N=2002;
     4 vector<int>ql[N],qr[N],qv[N],Ql[N],Qr[N],Qv[N];
     5 int t,n,m,q;
     6 int mo(int x){return x>=mod?x-mod:x;}
     7 int add(int&x,int y){x=mo(x+y);}
     8 int dec(int&x,int y){x=mo(x-y+mod);}
     9 struct D{
    10     int w,p;
    11     D operator+(D x){return w==x.w?(D){w,mo(p+x.p)}:(w>x.w?*this:x);}
    12 }w[N][N],E,ans;
    13 int nt[N<<2],lim[N],lp[N][N],up[N][N];
    14 #define lc p<<1
    15 #define rc lc|1
    16 #define md (L+R>>1)
    17 void build(int p=1,int L=1,int R=1){
    18     nt[p]=N;if(L==R)return;
    19     build(lc,L,md);build(rc,md+1,R);
    20 }
    21 void add(int l,int r,int x,int p,int L,int R){
    22     if(x>nt[p])return;
    23     if(l<=L&&R<=r){nt[p]=min(nt[p],x);return;}
    24     if(l<=md)add(l,r,x,lc,L,md); if(r>md)add(l,r,x,rc,md+1,R);
    25 }
    26 void print(int x,int p,int L,int R){
    27     x=min(x,nt[p]);if(L==R){lim[L]=x;return;}
    28     print(x,lc,L,md);print(x,rc,md+1,R);
    29 }
    30 struct que{
    31     int buc[N],pos[N],h,t;D v[N];
    32     void push(int i,D x){
    33         while(t>=h&&v[t].w<x.w)buc[v[t--].w]=0;
    34         add(buc[x.w],x.p);v[++t]=x;pos[t]=i;
    35     }
    36     D top(int l){
    37         while(t>=h&&pos[h]<l)dec(buc[v[h].w],v[h].p),h++;
    38         return h>t?(D){0,0}:(D){v[h].w,buc[v[h].w]};
    39     }
    40     void clear(){while(t>=h)buc[v[t--].w]=0;h=1;t=0;}
    41 }qC[N],qR[N];
    42 int main(){scanf("%d",&t);while(t--){
    43     scanf("%d%d%d",&n,&m,&q);
    44     for(int i=1;i<=m;++i)ql[i].clear(),qr[i].clear(),qv[i].clear(),qC[i].clear();
    45     for(int i=1;i<=n;++i)Ql[i].clear(),Qr[i].clear(),Qv[i].clear(),qR[i].clear();
    46     for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)w[i][j]=E;ans=E;
    47     for(int i=1;i<=q;++i){
    48         int a,b,c,d;scanf("%d%d%d%d",&a,&b,&c,&d);
    49         if(a==c||b==d)continue;
    50         Ql[c].push_back(b+1);Qr[c].push_back(d);Qv[c].push_back(a);
    51         ql[d].push_back(a+1);qr[d].push_back(c);qv[d].push_back(b);
    52     }
    53     build(1,1,n);
    54     for(int i=m;i;--i){
    55         for(int j=0;j<ql[i].size();++j)add(ql[i][j],qr[i][j],qv[i][j],1,1,n);
    56         print(m,1,1,n); for(int j=1;j<=n;++j)lp[j][i]=min(i,lim[j]);
    57     }
    58     build(1,1,m);
    59     for(int i=n;i;--i){
    60         for(int j=0;j<Ql[i].size();++j)add(Ql[i][j],Qr[i][j],Qv[i][j],1,1,m);
    61         print(n,1,1,m); for(int j=1;j<=m;++j)up[i][j]=min(i,lim[j]);
    62     }
    63     for(int i=1;i<=n;++i)for(int j=1;j<=m;++j){
    64         w[i][j]=(D){0,1};
    65         if(i>1&&j>1)qR[i-1].push(j-1,w[i-1][j-1]); if(j>1&&i>2)qC[j-1].push(i-2,w[i-2][j-1]);
    66         w[i][j]=w[i][j]+qR[i-1].top(lp[i][j])+qC[j-1].top(up[i][j]);
    67         w[i][j].w++;ans=ans+w[i][j];
    68     }printf("%d %d
    ",ans.w,ans.p);
    69 }}
    View Code

    T2:特立独行的图

    大意:给定图,构造序列$A$及偶数$L$,满足$forall i eq j,0 < |A_i - A_j | le L$。且满足$i,j$右边当且仅当$|A_i - A_j |le frac{L}{2}$。$n le 10^3$

    要求$L le 2 imes 10^9,|A_i| le 10^9$

    首先,把所有$A$同时加上同一个数整个图没有变化。

    所以说为了方便,我们可以把值域锁定在$[-frac{L}{2},frac{L}{2}]$。

    发现,负数与负数之间不可能连边,正数和正数之间也没有连边,正数和负数之间可能有也可能没有。

    先不考虑$A_i=0$的情况。那么所有数一定可以分成正数和负数两组,内部都没有边。

    就是二分图而已。染色判一下就行。

    然后对于两个负数$A_i,A_j$。如果有$A_i < A_j$。那么显然$j$的出边集合含于$i$的出边集合。

    所以我们可以对于正负两部点分别按照度数排序,然后判断相邻两个集合是否为包含关系。($bitset$)

    如果都是包含关系,那么我们就能确定所有$A$的相对大小关系(连边情况相同的点相对大小随意)

    然后只需要从小到大枚举正数,枚举其所有出边依次分配权值即可构造出一组合法解。

    然而存在$A_i=0$的情况。这时候图中会出现恰好一个三元环。特盘一下就可以了。

    下面这份代码将$stable sort$换成$sort$后会$RE$。还请知道原因的大神指点迷津。

    大神们告诉我是自定义比较函数必须是小于而不是小于等于,学会了,谢谢大神。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define fail {puts("No");goto F;}
     4 int t,n,m,fir[1005],l[1000005],to[1000005],ec,co[1005],r[1005],cnt,sz[1005],tim,ans[1005],A,Z,C,ok;
     5 bitset<1001>B[1001];
     6 bool cmp(int a,int b){return sz[a]<=sz[b]||b==A;}
     7 void link(int a,int b){l[++ec]=fir[a];fir[a]=ec;to[ec]=b;}
     8 void dfs(int p,int c){
     9     co[p]=c;
    10     for(int i=fir[p];i;i=l[i])if(!co[to[i]])dfs(to[i],c^1);else if(co[to[i]]==co[p])ok=0;
    11 }
    12 int main(){scanf("%d",&t);while(t--){
    13     scanf("%d%d",&n,&m);
    14     for(int i=1,a,b;i<=m;++i)scanf("%d%d",&a,&b),link(a,b),link(b,a),B[a][b]=B[b][a]=1,sz[a]++,sz[b]++;
    15     for(int i=1;i<=n;++i)for(int j=fir[i];j;j=l[j])if(to[j]>i)for(int k=fir[to[j]];k;k=l[k])if(to[k]>to[j]&&B[i][to[k]])co[i]=co[to[j]]=co[to[k]]=1;
    16     
    17     int tot=0; for(int i=1;i<=n;++i)if(co[i])tot++,C=Z,Z=A,A=i;
    18     if(tot>3)fail;
    19     if(tot) if(sz[A]==2)swap(A,Z);else if(sz[C]==2)swap(Z,C);else if(sz[Z]!=2)fail;
    20     co[A]=co[C]=0;
    21     
    22     ok=1; for(int i=1;i<=n;++i)if(!co[i])dfs(i,2);
    23     if(!ok)fail;
    24     for(int i=1;i<=n;++i)if(co[i]==2)r[++cnt]=i;
    25     
    26     if(tot){
    27         int mx=0;
    28         for(int i=fir[A];i;i=l[i])mx=max(co[to[i]],mx);
    29         if(mx<=2)swap(A,C);
    30         for(int i=fir[A];i;i=l[i])if(co[to[i]]==2)fail;
    31         for(int i=fir[C];i;i=l[i])if(co[to[i]]==3)fail;
    32         if(sz[A]!=n-cnt||sz[C]!=1+cnt)fail;
    33         ans[C]=-1000000000;
    34     }
    35 //    cerr<<cnt<<endl;
    36 //    for(int i=1;i<=cnt;++i)cerr<<r[i]<<endl;
    37     stable_sort(r+1,r+1+cnt,cmp);
    38     for(int i=1;i<cnt;++i)if((B[r[i]]|B[r[i+1]])!=B[r[i+1]])fail;
    39     for(int i=1;i<=cnt;++i){
    40         for(int j=fir[r[i]];j;j=l[j])if(!ans[to[j]])ans[to[j]]=(++tim)-1000000000;
    41         ans[r[i]]=++tim;
    42     }
    43     printf("Yes
    2000000000 ");ans[A]=1000000000;ans[Z]=0;
    44     for(int i=1;i<=n;++i)printf("%d ",ans[i]);puts("");
    45     F:
    46     for(int i=1;i<=n;++i)fir[i]=co[i]=ans[i]=sz[i]=0,B[i].reset();
    47     tim=cnt=ec=A=C=Z=0;
    48 }}
    View Code

    T3:玩游戏

    大意:求调和级数$k$次前缀和。$n le 10^{15},k le 50,frac{|yourans-stdans|}{stdans} < 10^{-10}$

    当然原题意不是这样。但是作为一道证明超过一页半的结论题,有谁在意过程呢?

    很奇怪要输出浮点数。因为用到了一个诡异的结论:$H(n)=0.577215664901352 + ln(n) + frac{1}{2n}$

    可以快速求一个调和级数的值。但是在$n$较小时误差较大。所以$le 10^6$可以直接预处理。

    问题在于怎么前缀和。所以构造生成函数$S_k(x)$。

    在此区分一下(弱智题解写的乱七八糟):$S_k(x)$表示多项式在$x$处的点值,$S_k[n]$表示多项式的$x^n$项系数。

    首先我们知道有一个级数求和:

    $ln(1-x) = sumlimits_{i=1}^{infty}  - frac{x^i}{i}$。

    右边的那个东西的相反数就是$A_i=frac{1}{i}$的生成函数。做一遍前缀和就是调和级数。

    然后众所周知如果我们要给一个多项式做前缀和,应该卷积上这个式子:

    $sumlimits_{i=0}^{infty} x^i$

    等差数列求和也是级数求和得到$frac{1}{1-x}$

    所以说调和级数的生成函数就是$frac{-ln(1-x)}{1-x}$。这也就是$S_0(x)$

    然后按照题意再做若干次前缀和的话同理就有$S_k(x)=frac{-ln(1-x)}{(1-x)^{k+1}}$

    考虑对$S_k(x)$求导:

    $S'_k(x)=(frac{-ln(1-x)}{(1-x)^{k+1}})'$

    $=frac{frac{(1-x)^{k+1}}{1-x} - ln(1-x)(k+1)(1-x)^k }{((1-x)^{k+1})^2}$(除法求导公式)

    $=frac{1}{(1-x)^{k+2}} - (k+1) frac{ln(1-x)}{(1-x)^{k+2}}$(两边拆开并约分)

    $=frac{1}{(1-x)^{k+2}} + (k+1) S_{k+1}(x)$(回代$S_{k}(x)$的生成函数式)

    这样我们貌似就发现了一个可以递归的形式。

    首先我们明显知道$S'_k[n-1] = n S_k[n]$

    同时,还根据上面推的那一大堆,我们也能表示出$S'_k[n-1]$

    前面的分式用级数求和捯回去(啊对是泰勒展开)是$(sumlimits_{i=0}^{infty} x^i )^{k+2}$

    要求这个东西的第$n-1$次系数,那么含义考虑,是每次可以走任意步,要求$k+2$次之内走了$n-1$步。

    经典插板得到$inom{n+k}{k+1}$

    式子的右半部分的系数就是$(k+1)S_{k+1}[n-1]$

    所以我们现在通过两种方式表示出了$S'_{k}[n-1]$。两种画等号得到:

    $n S_k[n]= inom{n+k}{k+1} + (k+1)S_{k+1}[n-1]$

    为了方便我们让$k++,n--$,然后稍作移项:

    $S_k[n] = frac{n+1}{k} S_{k-1}[n+1] - frac{inom{n+k}{k}}{k}$

    $S_0[n]$我们可以用调和级数的公式轻松求得。组合数暴力求,就可以递归得到答案了。

    时间复杂度$O(k^2)$。

    题解给出了另一种没有证明的方法,在此记录一下:$ans=frac{n^k}{k!} (S_0(n)-S_0(k))$

     1 #include<iostream>
     2 #include<cmath>
     3 using namespace std;
     4 long double H[1000005];
     5 long double h(long long x){return x>1000000?.577215664901352L+log(x)+.5L/x:H[x];}
     6 long double S(int k,long long n){
     7     if(!k)return h(n);
     8     long double C=1.L/k;
     9     for(int i=1;i<=k;++i)C=C*(n+i)/i;
    10     return S(k-1,n+1)/k*(n+1)-C;
    11 }
    12 int main(){
    13     cout.unsetf(ios::fixed),cout.setf(ios::scientific),cout.precision(9);
    14     for(int i=1;i<=1000000;++i)H[i]=H[i-1]+1.L/i;
    15     long long n,k;cin>>k>>n;
    16     cout<<S(k,n);
    17 }
    View Code
  • 相关阅读:
    iOS-深入理解(转载)
    iOS开发
    夜光遥感
    希尔伯特曲线在地图图像分割中的应用
    希尔伯特曲线
    NLP生成论文
    MapGIS SDK(C++)【基础篇】
    从npm到vue和nodejs
    分形在遥感和GIS中的应用
    MapReduce、Hadoop、PostgreSQL、Spark
  • 原文地址:https://www.cnblogs.com/hzoi-DeepinC/p/12844282.html
Copyright © 2020-2023  润新知