A. 数论
数学题,经实践证明,这个题可以$AC$。
考试时打的暴力,拿到$20$分。
正解:
虽然现在思路还是有点模糊,但是大体的思路应该差不多。
首先,就像题解说的,如果对于一个非良好数$x$,$xp^c$也是非良好数,其中$p$为质数,$c>=0$。
前提是$x$中不含质因子$p$。
$xp^c$的约数个数$val[xp^c]$是$val[x] imes (c-1)$。
$x$中没有质因子$p$,当然他的约数中也没有质因子$p$,所以每次乘$p$都会形成新的约数。
之后就是一些玄学的操作。
每次"迭代"$($新名词$)$,把原序列中的数拓展,
先枚举素数,再枚举原序列中的数,再枚举指数,把新数存到临时的数组中。
最后删去新的临时数组中不合法的数,因为这个数不会再更新别的数了。
注意要排序。
再维护一个堆,维护前$k+1$大的约数个数。
这样就可以删掉当前不合法的数了,应该是当前,因为之后插进来的数还可能再使它不合法。
最后一定可以筛掉所有不合法的数。
最后$TLE70$。
丑陋的代码:
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #include<queue> #define _min(x,y) ((x)<(y)?(x):(y)) #define _max(x,y) ((x)>(y)?(x):(y)) #define _abs(x) ((x)<0?(-1*(x)):(x)) #define Maxn 1000050 #define Reg register //#define int long long #define int __int128 using namespace std; bool vis[Maxn]; int T,K,m,tot,top[2],pri[Maxn],ans[Maxn]; struct Node {int x,cnt;} stack[2][Maxn],lss[Maxn]; bool comp(Node a,Node b) {return a.x<b.x;} priority_queue<Node,vector<int>,greater<int> > q; void print(int x) { if(x>=10) print(x/10); putchar(x%10+48); return; } signed main() { scanf("%lld%lld%lld",&T,&K,&m); for(Reg int i=2;i<=10000;++i) { if(vis[i]) continue; pri[++tot]=i; if(tot==300) break; for(Reg int j=2;j*i<=100000;++j) vis[j*i]=1; } stack[0][++top[0]]=(Node){1,1}; for(Reg int i=1;i<=tot;++i) { int cur=i&1,pre=(i+1)&1; top[cur]=0; for(Reg int j=1;j<=top[pre];++j) { int lss=1,x=stack[pre][j].x,p=stack[pre][j].cnt; for(Reg int k=0;k<=60;++k) { if(lss*x>m||lss*x<0) break; stack[cur][++top[cur]]=(Node){lss*x,p*(k+1)}; lss*=pri[i]; } } sort(stack[cur]+1,stack[cur]+top[cur]+1,comp); int tok=0; while(!q.empty()) q.pop(); for(Reg int j=1;j<=top[cur];++j) { if(stack[cur][j].x==1) { if(q.size()>=K+1&&stack[cur][j].cnt-1<q.top()) continue; lss[++tok]=stack[cur][j]; q.push(stack[cur][j].cnt-1); while(q.size()>K+1) q.pop(); } else { if(q.size()>=K+1&&stack[cur][j].cnt-2<q.top()) continue; lss[++tok]=stack[cur][j]; q.push(stack[cur][j].cnt-2); while(q.size()>K+1) q.pop(); } } top[cur]=0; for(Reg int j=1;j<=tok;++j) stack[cur][++top[cur]]=lss[j]; } // for(Reg int i=1;i<=top[tot&1];++i) cout<<stack[tot&1][i].x<<endl; while(T--) { int x; scanf("%lld",&x); print(stack[tot&1][x].x); puts(""); } return 0; }
B. 位运算
正解:
用$dp[i][j]$表示第$i$个数,到这时一共有$j$个$1$,这种情况是否存在$(0/1)$。
记录一下前趋,因为只要一组合法解,所以出现一个记录一个就好了。
转移的话分三种情况:
符号为$&$,这时下界是$max(0,j+a[i+1]-m)$,上界是$min(j,a[i+1])$,
符号为$|$,这时下界是$max(j,a[i+1])$,上界是$min(m,j+a[i+1])$,
符号为^,这时下界是$|j-a[i+1]|$,上界是$2m-j-a[i+1]$。
就是这样,然后记录前趋。
最后用$dfs$还原原数,很麻烦,很多细节。
丑陋的代码:
#include<iostream> #include<cstring> #include<cstdio> #define _min(x,y) ((x)<(y)?(x):(y)) #define _max(x,y) ((x)>(y)?(x):(y)) #define _abs(x) ((x)<0?(-1*(x)):(x)) #define Maxn 100050 #define Reg register int n,m,c,opt[Maxn],dp[Maxn][35],pre[Maxn][35],A[Maxn],num[Maxn]; char s[10]; int get(int x) { int s=0; while(x) {if(x&1) ++s; x>>=1;} return s; } void dfs(int now,int st) { if(now==1) {num[1]=st; return;} if(opt[now-1]==1) { int nxt=pre[now][get(st)],nst=st,npt=st; for(Reg int i=1;i<=m;++i) { if(get(nst)==nxt) break; if(!(nst&(1<<(i-1)))) nst|=(1<<(i-1)); } for(Reg int i=1;i<=m;++i) { if(get(npt)==A[now]) break; if(((st&(1<<(i-1)))&&!(nst&(1<<(i-1))))) npt|=(1<<(i-1)); else if((!(st&(1<<(i-1)))&&!(nst&(1<<(i-1))))) npt|=(1<<(i-1)); } num[now]=npt; dfs(now-1,nst); } else if(opt[now-1]==2) { int nxt=pre[now][get(st)],nst=st; for(Reg int i=1;i<=m;++i) { if(get(nst)==nxt) break; if(nst&(1<<(i-1))) nst^=(1<<(i-1)); } int npt=nst^st; for(Reg int i=1;i<=m;++i) { if(get(npt)==A[now]) break; if(nst&(1<<(i-1))) npt|=(1<<(i-1)); } num[now]=npt; dfs(now-1,nst); } else { int nxt=pre[now][get(st)],nst=0,npt=0; for(Reg int i=1;i<=m;++i) { if(!(st&(1<<(i-1)))) continue; if(get(nst)<nxt) nst|=(1<<(i-1)); else npt|=(1<<(i-1)); } for(Reg int i=1;i<=m;++i) { if(nxt-get(nst)==A[now]-get(npt)) break; if(nxt-get(nst)>A[now]-get(npt)&&(npt&(1<<(i-1)))&&!(nst&(1<<(i-1)))) nst^=(1<<(i-1)),npt^=(1<<(i-1)); if(nxt-get(nst)<A[now]-get(npt)&&(nst&(1<<(i-1)))&&!(npt&(1<<(i-1)))) nst^=(1<<(i-1)),npt^=(1<<(i-1)); } for(Reg int i=1;i<=m;++i) if(!(st&(1<<(i-1)))&&get(nst)<nxt&&get(npt)<A[now]) nst|=(1<<(i-1)),npt|=(1<<(i-1)); num[now]=npt; dfs(now-1,nst); } return; } int main() { scanf("%d%d%d",&n,&m,&c); for(Reg int i=1;i<=n-1;++i) { scanf("%s",s); if(s[0]=='A') opt[i]=1; else if(s[0]=='O') opt[i]=2; else opt[i]=3; } for(Reg int i=1;i<=n;++i) scanf("%d",&A[i]); dp[1][A[1]]=1; for(Reg int i=1;i<=n-1;++i) { for(Reg int j=0;j<=m;++j) { if(!dp[i][j]) continue; if(opt[i]==1) for(Reg int k=_max(0,j-(m-A[i+1]));k<=_min(j,A[i+1]);++k) dp[i+1][k]=1,pre[i+1][k]=j; else if(opt[i]==2) for(Reg int k=_max(j,A[i+1]);k<=_min(m,j+A[i+1]);++k) dp[i+1][k]=1,pre[i+1][k]=j; else { int mxx=(m-A[i+1]>j)?(A[i+1]+j):(2*m-j-A[i+1]); for(Reg int k=_abs(j-A[i+1]);k<=mxx;k+=2) dp[i+1][k]=1,pre[i+1][k]=j; } } } if(!dp[n][get(c)]) printf("OvO"); else { dfs(n,c); for(Reg int i=1;i<=n;++i) printf("%d ",num[i]); } return 0; }
C. 旅行
总结:
这次考试题目挺好的,做题改题都挺费劲的。
其实这次考试也没必要谈心态,因为心态好了也想不出来。。。
说考试过程吧,
前$40$分钟看$T1$,开始打$k=0$的表,企图能找到一些规律。
发现前几项非常像等差数列。。其实没什么规律。
而且暴力分给的非常少。
之后看$T2$,发现居然有暴力分。。
打完搜索,然后看特殊性质$1$,应该能拿到一些分。
然后码了一个小时左右,嗯,以为$T2$的暴力分能拿满。
最后$T3$,心想可能教练故意难度递减排序,应该可做,然后想了一会儿,还是不可做。。
滚回去看$T1$,依然没什么思路,快速码了一个暴力,水到$20$分。
最后$20+10+0=30$。
没什么水平。。。