2018.8.21提高A&省选组模拟考试
为什么8月份的考试拖到现在才发题解呢?
因为实际上这是我们昨天才考的...只不过正好和某校8月份的模拟考试重题而已...
T1 题意简述:jzoj5835
解题思路:很简单的一道筛法题。
分析题目,发现只需要筛出[1,min(K,sqrt(R))]范围内的质数即可。
#include<algorithm>//STL通用算法 #include<bitset>//STL位集容器 #include<cctype> #include<cmath> #include<complex> #include<cstdio> #include<cstdlib> #include<cstring> #include<ctime> #include<deque>//STL双端队列容器 #include<list>//STL线性列表容器 #include<map>//STL映射容器 #include<iostream> #include<queue>//STL队列容器 #include<set>//STL集合容器 #include<stack>//STL堆栈容器 #include<utility>//STL通用模板类 #include<vector>//STL动态数组容器 #define INF 0x3f3f3f3f #define ll long long using namespace std; ll L,R,n,len,ans,sch[10000005]; ll cnt,pri[10000005],jdg[10000005]; void getpri() { jdg[1]=1; for(ll i=2;i<=n;i++) { if(!jdg[i]) pri[++cnt]=i; for(ll j=1;j<=cnt;j++) { if(pri[j]*i>n) break; jdg[i*pri[j]]=1; if(!(i%pri[j])) break; } } } int main() { freopen("prime.in","r",stdin); freopen("prime.out","w",stdout); scanf("%lld%lld%lld",&L,&R,&n); n=min((ll)sqrt(R),n),len=R-L+1; getpri(); for(ll i=1;i<=cnt;i++) { ll tmp=(L/pri[i]+(L%pri[i]!=0))*pri[i]-L+1; while(tmp<=len) sch[tmp]=1,tmp+=pri[i]; if(pri[i]>=L) sch[pri[i]-L+1]=0; } for(ll i=1;i<=len;i++) if(!sch[i]) ans^=L+i-1; printf("%lld\n",ans); return 0; }
T2 题意简述:jzoj5836
解题思路:贪心+矩阵乘法。
首先考虑如何计算一个序列中的子序列个数。(子序列个数指互不重复的子序列个数。以下同理)
题解中所给的式子是dp[num[i]]=∑(j=1,k)dp[j],其中dp[i]表示以i结尾的子序列个数。
其实有一种式子更易理解:dp[i]=dp[i-1]*2-dp[pos[num[i]]-1](pos[i]!=0)。
其中dp[i]表示序列第i项的子序列个数,pos[i]表示数字i在序列中上一次出现的位置。
这个式子的意思是,在上一项的每个子序列后面都填上一个num[i],但是还要减去重复的。
重复的子序列其实就是上次此数字出现时已经计入的子序列。当然,若还未出现过就不必减去了。
用这个方法可以算出整个序列的子序列个数。
接下来考虑m个待填数字。
观察式子,发现要使结果最大,只需使dp[pos[num]-1]最小即可。
易知,dp数组是单调递增的。因此,只需保证pos[num]-1最小便可保证结果最大。
也就是说,每次取最早出现的那个元素作为当前项即可。
直接递推复杂度O(n+m),这样的复杂度是无法接受的。考虑优化。
众所周知,递推式可以用矩阵乘法优化。最终复杂度O(n+k+k^3logm)。
为什么会有个(+k)?因为一开始的k项是无法用矩阵乘法优化的。
pos在一开始的k项取值范围为[0,n],显然我们无法建一个n*n的矩阵。
但是在经历一个循环节后,pos的取值范围就变成了[n+1,n+k],因此只需建一个(k+1)*(k+1)的矩阵即可。
#include<algorithm>//STL通用算法 #include<bitset>//STL位集容器 #include<cctype> #include<cmath> #include<complex> #include<cstdio> #include<cstdlib> #include<cstring> #include<ctime> #include<deque>//STL双端队列容器 #include<list>//STL线性列表容器 #include<map>//STL映射容器 #include<iostream> #include<queue>//STL队列容器 #include<set>//STL集合容器 #include<stack>//STL堆栈容器 #include<utility>//STL通用模板类 #include<vector>//STL动态数组容器 #define INF 0x3f3f3f3f #define ll long long #define MOD 1000000007 using namespace std; ll n,m,k,ans,num[1000010],pos[110],que[110],dp[2000010]; struct uio{ ll pos,num; }srt[110]; struct oiu{ ll sqr[110][110]; oiu(){memset(sqr,0,sizeof(sqr));} friend oiu operator *(const oiu &x,const oiu &y) { oiu z; for(ll i=1;i<=k+1;i++) for(ll j=1;j<=k+1;j++) for(ll l=1;l<=k+1;l++) (z.sqr[i][j]+=x.sqr[i][l]*y.sqr[l][j]%MOD)%=MOD; return z; } }mat,bgn; bool cmp(uio x,uio y){return x.pos<y.pos;} void init(oiu &x) {for(ll i=1;i<=k+1;i++) x.sqr[i][i]=1;} oiu qpow(oiu x,ll y) { oiu z;init(z); while(y) {if(y&1) z=z*x;x=x*x;y/=2;} return z; } signed main() { freopen("sequence.in","r",stdin); freopen("sequence.out","w",stdout); scanf("%lld%lld%lld",&n,&m,&k); for(ll i=1;i<=n;i++) scanf("%lld",&num[i]); dp[0]=1; for(ll i=1;i<=n;i++) { if(pos[num[i]]) dp[i]=(dp[i-1]*2-dp[pos[num[i]]-1]+MOD)%MOD; else dp[i]=dp[i-1]*2%MOD; pos[num[i]]=i; } for(ll i=1;i<=k;i++) srt[i]={pos[i],i}; sort(srt+1,srt+1+k,cmp); for(ll i=1;i<=k;i++) que[i]=srt[i].num; for(ll i=n+1;i<=n+min(m,k);i++) { ll x=que[i-n]; if(pos[x]) dp[i]=(dp[i-1]*2-dp[pos[x]-1]+MOD)%MOD; else dp[i]=dp[i-1]*2%MOD; pos[x]=i; } m-=k;if(m<=0) {printf("%lld\n",dp[n+m+k]-1);return 0;} mat.sqr[1][1]=2,mat.sqr[k+1][1]=-1; for(ll i=1;i<=k;i++) mat.sqr[i][i+1]=1; for(ll i=1;i<=k+1;i++) bgn.sqr[1][i]=dp[n+k-i+1]; mat=qpow(mat,m); for(ll i=1;i<=k+1;i++) (ans+=bgn.sqr[1][i]*mat.sqr[i][1]%MOD+MOD)%=MOD; printf("%lld\n",(ans-1+MOD)%MOD); return 0; }
T3 题意简述:jzoj5837
解题思路:不会。博主看了题解后还是没写出来。
在这里贴一个官方题解,有兴趣的大佬可了解一下。