[考试反思]0226省选模拟32:磨灭

被签到题$min25$爆干,被水题卡常,被恶心题吓走。

其实$T3$本来应该拿$30$然而居然过掉了第一档然后搜索写挂了。

$(!(($写成了$$$((!($然后就丢了$20$分。

然而$T1$也还有$10$分的部分分没有来得及打,一个简单的自然数幂和。

于是三道题的确都有一个橘红色的分数,加起来没有人家会的一个$min23$或者一个分类讨论题得的分多。

$min25$筛到也不是不会,可是当时只学了思想并没有打板子。于是考场上并不敢花时间去现场实现。。。

于是考场上一眼秒这大概是个$min25$然后就因为不会写而丢掉了。。。

主要时间砸在$T2$上了,思路挺简单但是对$256$的内存感到不可思议,于是在那里边想边卡花了不少时间。

$T3$没怎么往正道上想,部分分跟正解并没有什么关系然后就被带偏了。

然后就没了。滚回去学$min25$板子就行了。

 

T1:送你一道签到题

大意:$\sum\limits_{i=1}^{n}i^k  \sum\limits_{a_1,a_2,a_3,...,a_n} [\prod\limits_{j=1}^{m} a_j =j] \sigma(a_1) \sigma(a_2) ... \sigma(a_m) ,n \le 10^{10},m \le 10^9,k \le 10^3$

一看这玩意是由若干积性函数拼起来的,猜测答案的$\sum\limits_{i=1}^{n} f(i)$中的$f(i)$也就是关于$i$的积性函数。

然后分解后发现它的确就是积性函数。在质数的值是个低次多项式($k$次),质数的某次幂的值也可以求。(类似背包再乘组合数)

于是不难想到$min25$筛去求前缀和然后这题就没了。

算是第一次写$min25$筛所以有不少想说的。。

首先$min25$筛的第一步求$g$。在这道题里我们并不把$m$个数当作限制而是枚举最终有几个值不是$1$然后乘组合数来统计方案。

所以我们要统计的实际上是$i^k$那一项。那么也就是个低次多项式了。

$g$数组的初值是前缀和,于是在这道题里用得到自然数幂和,伯努利数或者插值任取。

然后统计分配的方案数需要设$dp[i][j]$表示$i$个位置一共选了$p^j$的情况。(积性函数只考虑每种质数处的取值所以是$p^e$的形式)

最后设$P(x)=\sum\limits_{i} dp[i][x] \binom{m}{i}$然后就可以在递归求解$S$的时候用了。

然后就是到底是否包含$<\sqrt{n}$的质数的问题,网上两种主流写法都是可以的但是貌似把质数保留的写法方便一些。

大概要处理的东西挺多,阶乘,逆元,$g$,伯努利数,方案数$dp$,$P$,质数处的幂和。然而一步一步来看的话并不是很难理解。

多做题也许就好了吧。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define Z 1111111
 4 #define ll long long
 5 #define mod 998244353
 6 ll n,v[Z],r;int m,k,g[Z],p[Z],pc,sq,o,x[Z],s[Z],X[Z],pw[Z],fac[Z],inv[Z],iv[Z],B[Z],dp[44][44],P[44],ppw[Z];
 7 int&get(ll a){return a<=sq?x[a]:X[n/a];}
 8 int C(int b,int t){return b<t||t<0?0:1ll*fac[b]*inv[t]%mod*inv[b-t]%mod;}
 9 int qp(ll b,int t,ll a=1){for(;t;t>>=1,b=b*b%mod)if(t&1)a=a*b%mod;return a;}
10 int spow(int n,ll a=0){for(int i=0,p=n;i<=k;++i)a=(a+1ll*C(k+1,i+1)*p%mod*B[k-i])%mod,p=1ll*p*n%mod; return a*iv[k+1]%mod;}
11 int S(ll x,int y,int a=0){
12     if(x<=1||p[y]>x)return 0;
13     for(int i=y,pe=1;1ll*p[i]*p[i]<=x&&i<=pc;++i)
14         for(ll e=1,p0=p[i],p1=p0*p0,rt=ppw[i]; p1<=x ; ++e)
15             a=(a+1ll*P[e]*rt%mod*S(x/p0,i+1)+1ll*P[e+1]*rt%mod*ppw[i])%mod, p0*=p[i],p1*=p[i],rt=rt*ppw[i]%mod;
16     return (a+1ll*P[1]*(g[get(x)]-pw[y-1]+mod))%mod;
17 }
18 int main(){
19     cin>>n>>m>>k;sq=sqrt(n);
20     for(int i=fac[0]=1;i<Z;++i)fac[i]=1ll*fac[i-1]*i%mod;
21     inv[Z-1]=qp(fac[Z-1],mod-2);
22     for(int i=Z-2;~i;--i)inv[i]=inv[i+1]*(i+1ll)%mod,iv[i+1]=1ll*fac[i]*inv[i+1]%mod;
23     for(int i=B[0]=1;i<k+3;++i)for(int j=0;j<i;++j)
24         B[i]=(B[i]-1ll*B[j]*C(i+1,j)%mod*iv[i+1]%mod+mod)%mod; B[1]++;
25     
26     for(int i=dp[0][0]=1;i<40;++i)for(int j=i-1;j<40;++j)for(int z=39;z>j;--z)
27         dp[i][z]=(dp[i][z]+dp[i-1][j]*(z-j+1ll))%mod;
28     for(int j=1;j<40;++j)for(int i=1,C=m;i<=j;++i)
29         P[j]=(P[j]+1ll*dp[i][j]*C)%mod,C=1ll*C*(m-i)%mod*iv[i+1]%mod;
30     
31     for(int i=2;i<=sq;++i){
32         if(!s[i])p[++pc]=i,ppw[pc]=pw[pc]=s[i]=qp(i,k);
33         for(int j=1;i*p[j]<=sq;++j){s[i*p[j]]=1ll*s[i]*s[p[j]]%mod;if(i%p[j]==0)break;}
34     }
35     for(int i=1;i<=sq;++i) s[i]=(s[i-1]+s[i])%mod;
36     for(int i=1;i<=pc;++i) pw[i]=(pw[i]+pw[i-1])%mod;
37     for(ll i=1,N,l;N=n/i,i<=n;i=l+1) l=n/N,v[get(N)=++o]=N,g[o]=N<=sq?s[N]:spow(N%mod)-1;
38     for(int i=1;i<=pc;++i)for(int j=1;j<=o&&1ll*p[i]*p[i]<=v[j];++j)
39         g[j]=(g[j]-1ll*ppw[i]*(g[get(v[j]/p[i])]-pw[i-1])%mod+mod)%mod;
40     cout<<S(n,1)+1<<endl;
41 }
View Code

 

T2:神渀

大意:给定一个$012$数列以及一个权值序列,强制在线求每个点为右端点的子区间中,满足$012$个数互不相同的区间的最大异或和。$n \le 3 \times 10^5$

首先这个限制非常奇怪,区间出现次数互不相同。

然而被不少题吊起来锤的我们总算会了点$trick$,直接差分。问题转变成差分也要互不相同。

互不相同不好处理,但是相同貌似就好处理很多。

最大异或和于是也不难想到建$01trie$。

于是我们只需要维护$trie$的每个节点上,每种相同的值出现了多少次就好了,然后就可以根据$size$判断是否可以往那个方向走。

然而我蠢了,为什么非得建一个$trie$然后上面维护那么多信息?时空常数太大。

对于每种值都建一棵$trie$就好了。

然而还是会被卡空间,又有一个神奇的$trick$.我们设$a=cnt0-cnt1,b=cnt1-cnt2,c=cnt0-cnt2$

那么我们实际上是对于$a,b,c,ab,bc,ac,abc$这$7$种值都建$trie$

然而实际上如果$ab$相同,那么就是$cnt1-cnt0,cnt2-cnt1$相同,那么就是$cnt2-cnt1+cnt1-cnt0=cnt2-cnt0=c$相同。

那么就是$abc$相同。所以说后四个$trie$是完全一样的可以压成一个,于是就卡过去了。

另外加一些优化,就是压一个儿子,或者压链。都能卡进$256MiB$的原限制。但是代码吗,就比较恶心了。。。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define S 300005ll
 4 #define Z 40000005
 5 map<int,int>rt[3];map<long long,int>RT;
 6 int p[S],A[S],n,c[2][Z],op,la,pc,cc[3],sz[Z],R;
 7 void insert(int&p,int w,int x=29){
 8     if(!p)p=++pc; sz[p]++; if(x<0)return;
 9     insert(c[w&1<<x?1:0][p],w,x-1);
10 }
11 int ask(int p,int p1,int p2,int p3,int p4,int w,int x=29){
12     if(x<0)return 0;
13     int o=w&1<<x?0:1;
14     if(sz[c[o][p]]!=sz[c[o][p1]]+sz[c[o][p2]]+sz[c[o][p3]]-2*sz[c[o][p4]])return ask(c[o][p],c[o][p1],c[o][p2],c[o][p3],c[o][p4],w,x-1)|1<<x;
15     o^=1;return ask(c[o][p],c[o][p1],c[o][p2],c[o][p3],c[o][p4],w,x-1);
16 }
17 int main(){
18     cin>>n>>op;
19     for(int i=1;i<=n;++i)scanf("%d",&p[i]);
20     for(int i=1;i<=n;++i)scanf("%d",&A[i]);
21     insert(R,0);insert(rt[0][0],0);insert(rt[1][0],0);insert(rt[2][0],0);insert(RT[0],0);
22     for(int i=1;i<=n;++i){
23         if(op)A[i]^=la,(p[i]^=la)%=3;cc[p[i]]++;A[i]^=A[i-1];
24         int a=cc[1]-cc[0],b=cc[2]-cc[1],c=cc[2]-cc[0];
25         la=(sz[rt[0][a]]+sz[rt[1][b]]+sz[rt[2][c]]-2*sz[RT[a*S+b]]==i)?0:ask(1,rt[0][a],rt[1][b],rt[2][c],RT[a*S+b],A[i]);
26         printf("%d ",la); insert(R,A[i]); insert(rt[0][a],A[i]); insert(rt[1][b],A[i]); insert(rt[2][c],A[i]); insert(RT[a*S+b],A[i]);
27     }
28 }
View Code

 

T3:外挂

大意:求$n\times m$的$01$矩阵中每个点,有多少种满足:所有的$1$可以被两个大小为$x \times y$的矩形完全覆盖。$n,m \le 60$

我也不知道为啥没想到。

首先为了不重不漏,我们缩小包围圈$nm$保证四个边界上一定有人。这样的话只需要两层枚举边框大小就行。

然后问题转化为:边框上一定有人且能被两个特定矩形覆盖的方案数。

发现最优的覆盖方案一定是放在左上角,右下角。或者放在右上角,左下角。

于是状压$6$维:是否有点已经在$4$个边界上,是否有点出现在了两种覆盖方法之外。

然后就没了,逐个点讨论填不填的影响就行了。时间复杂度$O(2^6n^2m^2)$

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define mod 998244353
 4 int ans,n,m,x,y,pw[3666],dp[2][1<<6];
 5 void add(int&a,int b){a+=b;if(a>=mod)a-=mod;}
 6 int cal(int n,int m){
 7     int L=0,N=1,X=n-x+1,Y=m-y+1;
 8     for(int s=0;s<1<<6;++s)dp[L][s]=0;dp[L][0]=1;
 9     for(int i=1;i<=n;++i)for(int j=1;j<=m;++j){
10         for(int s=0;s<1<<6;++s)dp[N][s]=0;
11         for(int s=0;s<1<<6;++s)add(dp[N][s|(i==1)<<5|(i==n)<<4|(j==1)<<3|(j==m)<<2|(!(i<=x&&j<=y)&&!(i>=X&&j>=Y))<<1|(!(i>=X&&j<=y)&&!(i<=x&&j>=Y))],dp[L][s]),add(dp[N][s],dp[L][s]);
12         L^=1;N^=1;
13     }return(0ll+dp[L][62]+dp[L][61]+dp[L][60])%mod;
14 }
15 int main(){
16     cin>>n>>m>>x>>y;
17     for(int i=pw[0]=1;i<=n*m;++i)pw[i]=(pw[i-1]<<1)%mod;
18     for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)ans=(ans+cal(i,j)*(n-i+1ll)%mod*(m-j+1))%mod;
19     cout<<1+ans<<endl;
20 }
View Code

 

posted @ 2020-02-27 17:15  DeepinC  阅读(...)  评论(...编辑  收藏