zoukankan      html  css  js  c++  java
  • 4.5&4.7联考题解

    本来想加个密码的,后来一想全HE就咱们这几个人,外省的dalao愿看也没事儿,就公开算了,省得加密码各种麻烦。

    先补这两天的题解吧……如果有空的话我可能会把上次联考的题解补上= =(中午没睡觉,现在困得很,根本没法写题……

    Day 1

    算算算number

    感觉出题人出题的时候zz了吧,费了半天搞出来一个极其麻烦还跑得慢的做法是要闹哪样啊……

    算了,写一写$O(nk)$做法的推导过程吧,虽然其实非常好推……

    首先定义$S_i$表示到$1~i$位置的前缀和,并且规定$S_0=0$,那么

    egin{align}Ans_i=&sum_{j=0}^{i-1}left(S_i-S_j ight)^k\=&sum_{j=0}^{i-1}sum_{x=0}^k C_k^xS_i^xleft(-S_j ight)^{k-x}left(二项式定理 ight)\=&sum_{x=0}^k C_k^x S_i^xsum_{j=0}^{i-1}left(-S_j ight)^{k-x}end{align}

    然后就很好搞了,首先枚举$x$,然后维护$left(-S_j ight)^{k-x}$的前缀和即可。组合数是可以直接一边算一边递推的,不需要再预处理了。

    直接算是$O(nklog k)$的,加上一些线性预处理逆元之类的技巧就可以降到$O(nk+nlog p)$了,实测极限数据只需要不到1.3s就可以出解,并且不需要利用数据随机这个性质。

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 using namespace std;
     5 const int maxn=50010,p=1000000007;
     6 int qpow(int,int);
     7 int fac[maxn],fac_inv[maxn],inv[maxn];
     8 int T,n,k,s[maxn],s_k[maxn],s_inv[maxn],t[maxn],a[maxn],ans[maxn];
     9 int main(){
    10     fac[0]=fac_inv[0]=1;
    11     for(int i=1;i<=50001;i++)fac[i]=(long long)fac[i-1]*i%p;
    12     fac_inv[50001]=qpow(fac[50001],p-2);
    13     for(int i=50001-1;i;i--)fac_inv[i]=(long long)fac_inv[i+1]*(i+1)%p;
    14     for(int i=1;i<=50001;i++)inv[i]=(long long)fac[i-1]*fac_inv[i]%p;
    15     scanf("%d",&T);
    16     while(T--){
    17         memset(s,0,sizeof(s));
    18         memset(ans,0,sizeof(ans));
    19         scanf("%d%d",&n,&k);
    20         for(int i=1;i<=n;i++){
    21             scanf("%1d",&s[i]);
    22             s[i]+=s[i-1];
    23             s_k[i]=qpow(s[i],k);
    24             s_inv[i]=qpow(s[i],p-2);
    25         }
    26         fill(t,t+n+1,1);
    27         for(int x=0,C=1;x<=k;x++){
    28             copy(t,t+n+1,a);
    29             for(int i=1;i<=n;i++){
    30                 a[i]=(a[i]+a[i-1])%p;
    31                 ans[i]=(ans[i]+(long long)C*s_k[i]%p*a[i-1]%p)%p;
    32             }
    33             for(int i=0;i<=n;i++){
    34                 t[i]=(long long)t[i]*((p-s[i])%p)%p;
    35                 s_k[i]=(long long)s_k[i]*s_inv[i]%p;
    36             }
    37             C=(long long)C*(k-x)%p*inv[x+1]%p;
    38         }
    39         for(int i=1;i<=n;i++)printf("%d ",ans[i]);
    40         printf("
    ");
    41     }
    42     return 0;
    43 }
    44 int qpow(int a,int b){
    45     int ans=1;
    46     for(;b;b>>=1,a=(long long)a*a%p)if(b&1)ans=(long long)ans*a%p;
    47     return ans;
    48 }
    49 /*
    50 2
    51 10 2
    52 3672415495
    53 10 2
    54 9040607879
    55 Answer:
    56 9 117 474 634 1066 1200 2075 3043 6238 8668
    57 81 81 201 201 633 633 1690 3802 6539 11435
    58 */
    View Code

    所以你们看,这题就是个基本的二项式定理,换句话说就是比较水的高考题……

    买买买buy

    T(o)B(e)C(ompleted)……

    树树树mst

    去%了一发gzz的代码,然后发现我连Prim都不会了……我好菜啊= =

    直接处理曼哈顿距离不太方便,可以坐标变换一下变成切比雪夫距离。

    每次连边选的是已选集合与未选集合之间最大的一条边,边的端点有四种情况:

    1.已选的最左点和未选的最右点

    2.已选的最右点和未选的最左点

    3.已选的最上点和未选的最下点

    4.已选的最下点和未选的最上点

    用set或者最大-最小堆维护所有没选的点的$x/y$坐标最大/最小值即可,复杂度$O(nlog n)$(再次吐槽一波出题人zz)。

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 #include<set>
     5 using namespace std;
     6 const int maxn=100010;
     7 multiset<pair<int,int> >mx,my;
     8 long long ans=0;
     9 int n,tx,ty,x[maxn],y[maxn],maxx,maxy,minx,miny;
    10 int main(){
    11     scanf("%d",&n);
    12     for(int i=1;i<=n;i++){
    13         scanf("%d%d",&tx,&ty);
    14         x[i]=tx+ty;
    15         y[i]=tx-ty;
    16     }
    17     maxx=minx=x[1];
    18     maxy=miny=y[1];
    19     for(int i=2;i<=n;i++){
    20         mx.insert(make_pair(x[i],y[i]));
    21         my.insert(make_pair(y[i],x[i]));
    22     }
    23     for(int k=1;k<n;k++){
    24         int w1=maxx-mx.begin()->first,w2=mx.rbegin()->first-minx,w3=maxy-my.begin()->first,w4=my.rbegin()->first-miny,w=max(max(w1,w2),max(w3,w4));
    25         if(w==w1){
    26             pair<int,int>t=*mx.begin();
    27             minx=min(minx,t.first);
    28             maxy=max(maxy,t.second);
    29             miny=min(miny,t.second);
    30             mx.erase(mx.begin());
    31             my.erase(my.find(make_pair(t.second,t.first)));
    32         }
    33         else if(w==w2){
    34             pair<int,int>t=*mx.rbegin();
    35             maxx=max(maxx,t.first);
    36             maxy=max(maxy,t.second);
    37             miny=min(miny,t.second);
    38             mx.erase(mx.find(*mx.rbegin()));
    39             my.erase(my.find(make_pair(t.second,t.first)));
    40         }
    41         else if(w==w3){
    42             pair<int,int>t=*my.begin();
    43             miny=min(miny,t.first);
    44             maxx=max(maxx,t.second);
    45             minx=min(minx,t.second);
    46             my.erase(my.begin());
    47             mx.erase(mx.find(make_pair(t.second,t.first)));
    48         }
    49         else if(w==w4){
    50             pair<int,int>t=*my.rbegin();
    51             maxy=max(maxy,t.first);
    52             maxx=max(maxx,t.second);
    53             minx=min(minx,t.second);
    54             my.erase(my.find(*my.rbegin()));
    55             mx.erase(mx.find(make_pair(t.second,t.first)));
    56         }
    57         ans+=w;
    58     }
    59     printf("%lld",ans);
    60     return 0;
    61 }
    View Code

    考场上想到了坐标变换和Prim,但是并没有去细想,自信自己LCT打得很熟练,码完30分大暴力之后就开始码LCT+随机化,结果费了将近一个小时,并且随机化效果还不如暴力,本来可能可以用来思考正解的宝贵时间就这么浪费了……

    没办法,自己弱不能怪社会……

     

    Day 2

    行了行了,暴力都能写错,我是制杖……

    A

    注意到一次操作对逆序对产生的影响就是把所有下标$ge x$的数与后面的数所产生的逆序对全部消掉,并且对一个位置操作后再次操作不会产生任何影响,因此可以定义每个数的贡献为它与后面的数产生的逆序对数,那么每次操作就会使答案减掉所有下标$ge x$且权值$le a_x$的数的贡献并把这些数的贡献移除。

    如果要直接维护的话可以用树套树或者K-D树。当然离线的话要容易的多,只要求出每个数的贡献消失的时刻就行了,显然这个就是每个数前面权值$ge a_x$的数中最早被操作的那个,鉴于都是后缀查询,树状数组维护即可。

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 using namespace std;
     5 const int maxn=100010;
     6 void add(int);
     7 int query_sum(int);
     8 void modify(int,int);
     9 int query_min(int);
    10 long long ans[maxn]={0};
    11 int n,m,a[maxn],b[maxn],c[maxn]={0},w[maxn],x;
    12 int main(){
    13     scanf("%d%d",&n,&m);
    14     for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    15     copy(a+1,a+n+1,b+1);
    16     sort(b+1,b+n+1);
    17     for(int i=n;i;i--){
    18         a[i]=lower_bound(b+1,b+n+1,a[i])-b;
    19         w[i]=query_sum(a[i]-1);
    20         ans[0]+=w[i];
    21         add(a[i]);
    22     }
    23     fill(b+1,b+n+1,(~0u)>>1);
    24     for(int i=1;i<=m;i++){
    25         scanf("%d",&x);
    26         b[x]=min(b[x],i);
    27     }
    28     fill(c+1,c+n+1,(~0u)>>1);
    29     for(int i=1;i<=n;i++){
    30         if(b[i]<=m)modify(a[i],b[i]);
    31         int t=query_min(a[i]);
    32         if(t<=m)ans[t]-=w[i];
    33     }
    34     printf("%lld
    ",ans[0]);
    35     for(int i=1;i<=m;i++){
    36         ans[i]+=ans[i-1];
    37         printf("%lld
    ",ans[i]);
    38     }
    39     return 0;
    40 }
    41 void add(int x){
    42     while(x<=n){
    43         c[x]++;
    44         x+=x&-x;
    45     }
    46 }
    47 int query_sum(int x){
    48     int ans=0;
    49     while(x){
    50         ans+=c[x];
    51         x&=x-1;
    52     }
    53     return ans;
    54 }
    55 void modify(int x,int d){
    56     while(x){
    57         c[x]=min(c[x],d);
    58         x&=x-1;
    59     }
    60 }
    61 int query_min(int x){
    62     int ans=(~0u)>>1;
    63     while(x<=n){
    64         ans=min(ans,c[x]);
    65         x+=x&-x;
    66     }
    67     return ans;
    68 }
    View Code

    考场上把暴力写挂了,本来在排序的时候应该把原来的权值复制一份拿来比较,我却直接拿修改后的权值比较,居然还有40分……(这种**错误都能犯,这人没救了……

    B

    首先给9个格子编号,定义$f_{i,j}$表示从$i$走$n$步之后到达$j$的方案数,显然这个是可以大力跑$9$遍矩阵快速幂求出来的,不过注意到每次的转移矩阵都一样,因此只跑一遍快速幂就行了,然后用$9$个不同的初始矩阵乘一下就可以得到最终的矩阵了(当然你利用对称性的话可以把矩乘次数再减少一些,不过并不需要卡这点常数……)。

    然后就很好搞了,反正总共只有$9$个格子, $O(9!)$大力枚举每个机器人最后走到了哪儿即可,复杂度$O(9!+9^4+9^3log n)$。

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 using namespace std;
     5 const int p=1000000007,dx[]={1,-1,0,0},dy[]={0,0,1,-1},
     6 id[3][3]={
     7 {0,1,2},
     8 {3,4,5},
     9 {6,7,8}
    10 };
    11 struct Matrix{
    12     int a[9][9];
    13     Matrix(int k=0){
    14         memset(a,0,sizeof(a));
    15         if(k)for(int i=0;i<9;i++)a[i][i]=k;
    16     }
    17     Matrix operator*(const Matrix &b)const{
    18         Matrix c;
    19         for(int i=0;i<9;i++)for(int j=0;j<9;j++)if(a[i][j]){
    20             for(int k=0;k<9;k++)if(b.a[j][k])c.a[i][k]=(c.a[i][k]+(long long)a[i][j]*b.a[j][k]%p)%p;
    21         }
    22         return c;
    23     }
    24     int *operator[](int x){return a[x];}
    25 }A,B;
    26 Matrix qpow(Matrix,long long);
    27 int f[9][9],a[9],ans=0;
    28 long long n;//long long!!!
    29 int main(){
    30     scanf("%lld",&n);
    31     for(int i=0;i<3;i++)for(int j=0;j<3;j++){
    32         B[id[i][j]][id[i][j]]=1;
    33         for(int k=0;k<4;k++){
    34             int x=i+dx[k],y=j+dy[k];
    35             if(x<0||x>=3||y<0||y>=3)continue;
    36             B[id[x][y]][id[i][j]]++;
    37         }
    38     }
    39     B=qpow(B,n);
    40     for(int i=0;i<9;i++){
    41         A=Matrix();
    42         A[0][i]=1;
    43         A=A*B;
    44         for(int j=0;j<9;j++)f[j][i]=A[0][j];
    45     }
    46     //for(int i=0;i<9;i++)for(int j=0;j<9;j++)printf("f[%d][%d]=%d
    ",i,j,f[i][j]);
    47     for(int i=0;i<9;i++)a[i]=i;
    48     do{
    49         int tmp=1;
    50         for(int i=0;i<9;i++)tmp=(long long)tmp*f[i][a[i]]%p;
    51         ans=(ans+tmp)%p;
    52     }while(next_permutation(a,a+9));
    53     printf("%d",ans);
    54     return 0;
    55 }
    56 Matrix qpow(Matrix a,long long b){
    57     Matrix ans(1);
    58     for(;b;b>>=1,a=a*a)if(b&1)ans=ans*a;
    59     return ans;
    60 }
    View Code

    C

    我说这是杜教筛板子题你信么……

    egin{align}Ans=&sum_{i=1}^nsum_{j=1}^n(i,j)^k\=&sum_{d=1}^n d^ksum_{i=1}^nsum_{j=1}^n[(i,j)=d]\=&sum_{d=1}^n d^kleft(2 S_varphileft(leftlfloorfrac nd ight floor ight)-1 ight)\&其中S_varphi(n)=sum_{i=1}^nvarphi(i)end{align}

    只要能算出$d^k$的前缀和就可以分块了,鉴于$k$很小,用差分组合数(牛顿插值)或者伯努利数之类的做法随便搞一搞就可以了。

    题解写的复杂度有误,杜教筛在预处理前$n^{frac 2 3}$项的$varphi(n)$前缀和之后是可以做到总复杂度$O(n^{frac 2 3})$的,即使外面套了一层分块也是如此(毕竟杜教筛自带分块形式)。

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 using namespace std;
     5 const int table_size=5000000,maxn=table_size+10,p=1000000007,inv_2=500000004;
     6 void phi_table(int);
     7 int S(long long);//long long!!!
     8 int s_k(long long);//long long!!!
     9 int qpow(int,int);
    10 bool notp[maxn]={false},vis[maxn]={false};
    11 int prime[maxn]={0},phi[maxn]={0},S_phi[maxn]={0};
    12 int k,s[10],r[10],inv[10];
    13 long long N;//IMPORTANT!!!Remember to use long long!!!
    14 int main(){
    15     scanf("%lld%d",&N,&k);
    16     for(int i=1;i<=k+2;i++){
    17         r[i]=qpow(i,k);
    18         s[i]=(r[i]+s[i-1])%p;
    19         inv[i]=qpow(i,p-2);
    20     }
    21     for(int j=3;j<=k+2;j++)for(int i=k+2;i>=j;i--)r[i]=(r[i]-r[i-1]+p)%p;
    22     /* for(;;){
    23         scanf("%lld",&N);
    24         printf("%d
    ",s_k(N));
    25     } */
    26     phi_table(min((long long)table_size,N));
    27     long long i=1,last;
    28     int ans=0,sk=0,tmp;
    29     while(i<=N){
    30         last=N/(N/i);
    31         tmp=s_k(last);
    32         ans=(ans+(tmp-sk+p)%p*(long long)((2*S(N/i)%p-1+p)%p)%p)%p;
    33         sk=tmp;
    34         i=last+1;
    35     }
    36     printf("%d",ans);
    37     return 0;
    38 }
    39 void phi_table(int n){
    40     phi[1]=1;
    41     for(int i=2;i<=n;i++){
    42         if(!notp[i]){
    43             prime[++prime[0]]=i;
    44             phi[i]=i-1;
    45         }
    46         for(int j=1;j<=prime[0]&&i*prime[j]<=n;j++){
    47             notp[i*prime[j]]=true;
    48             if(i%prime[j])phi[i*prime[j]]=phi[i]*(prime[j]-1);
    49             else{
    50                 phi[i*prime[j]]=phi[i]*prime[j];
    51                 break;
    52             }
    53         }
    54     }
    55     for(int i=1;i<=n;i++)phi[i]=(phi[i]+phi[i-1])%p;
    56 }
    57 int S(long long n){
    58     if(n<=table_size)return phi[n];
    59     if(vis[N/n])return S_phi[N/n];
    60     vis[N/n]=true;
    61     int ans=n%p*((n+1)%p)%p*inv_2%p;
    62     long long i=2,last;//long long!!!
    63     while(i<=n){
    64         last=n/(n/i);
    65         ans=(ans-(long long)S(n/i)*((last-i+1)%p)%p+p)%p;
    66         i=last+1;
    67     }
    68     S_phi[N/n]=ans;
    69     return ans;
    70 }
    71 int s_k(long long n){
    72     if(n<=k+2)return s[n];
    73     int ans=0,C=1;
    74     for(int i=0;i<=k+1;i++){
    75         ans=(ans+(long long)C*r[i+1]%p)%p;
    76         C=(long long)C*((n-i-1)%p)%p*inv[i+1]%p;
    77     }
    78     return ans;
    79 }
    80 int qpow(int a,int b){
    81     int ans=1;
    82     for(;b;b>>=1,a=(long long)a*a%p)if(b&1)ans=(long long)ans*a%p;
    83     return ans;
    84 }
    View Code

    ps:关于$ sum_{i=1}^nsum_{j=1}^n[(i,j)=d]=2S_varphileft(leftlfloorfrac n d ight floor ight)+1$的推导:

    egin{align}&sum_{i=1}^nsum_{j=1}^n[(i,j)=d]\=&2left(sum_{i=1}^nsum_{j=1}^i[(i,j)=d] ight)-1\=&2left(sum_{d|i}^nsum_{d|j,jle i}left[left(frac i d,frac j d ight)=1 ight] ight)-1\=&2left(sum_{i=1}^{leftlfloorfrac n d ight floor}sum_{j=1}^i[(i,j)=1] ight)-1\=&2left(sum_{i=1}^{leftlfloorfrac n d ight floor}varphi(i) ight)-1\=&2S_varphileft(leftlfloorfrac n d ight floor ight)-1\end{align}

    (好吧,感觉大多人都应该知道这个结论,真是废话……)

     

    反思:

    Day1在T3的LCT+随机化上浪费了好多时间(真是奇怪,为什么我的LCT有时候能不到半个小时写完不用调,有的时候又要调上大半个小时才能写出来……),导致没有时间细想T3的正解,Day2因为太久不写杜教筛和差分组合数生疏了,在推杜教筛求$S_varphi(n)$以及求$k$次幂之和上浪费了不少时间,结果写完后两题和A的暴力之后已经没有时间去想A的正解了,两天都是把时间浪费在不应该的地方,这种考试策略的错误一定要注意。

    这次联考也再次让我认识到了自己思维的薄弱之处,这些天一定要多加锻炼,不要再看到思维题就束手无策。

    话说两天都挂成这样还能压线进A队是要闹哪样……

  • 相关阅读:
    一些算法思路整理
    (递归描述)根据上排给出十个数,在其下排填出对应的十个数
    在二元树中找出和为某一值的所有路径(树)--最容易理解的版本?
    动态规划求解连续子数组最大和问题(应该是新的描述方法?)
    ubuntu/linux 下 git 通过代理下载数据 (最简单的方式)
    3. Longest Substring Without Repeating Characters(c++) 15ms
    1.Two Sum(c++)(附6ms O(n) accepted 思路和代码)
    3篇NeuroImage文献分析
    PCA、ZCA白化
    mysql创建全文索引
  • 原文地址:https://www.cnblogs.com/hzoier/p/6670780.html
Copyright © 2011-2022 走看看