【比賽報告】CYJian的水題大賽[第三彈]
阿新 • • 發佈:2018-12-13
A.食堂
賽後題解把我秀到了,中間這段變形我是怎麼也想不到的。
總結
當時做這道題寫了兩小時直接炸飛。首先就是這個取小數部分處理不來,然後就肯定GG了。 從這題能學到的:取小數部分的處理、公式變形(重點)、多次字首和求解。
B.乘積
賽後題解 利用斐波拉契數列相減找到位移量再線段樹維護區間最小值,秀的飛起。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define lc (o<<1) #define rc (o<<1|1) const int N=5e6+10,T=1e6+10,K=1e4+10; int t,prime[K*40],p,fib[50],g[K],ans[K],mn[K<<5],lazy[K<<5],pos[K<<5],Id[K<<5],res[T]; bool iscomp[N]; short cnt[N]; struct node{ int n,k,id; bool operator <(const node&rhs)const{ return n<rhs.n;} }ask[T]; void primetable() { for(int i=2;i<N;i++) { if(!iscomp[i])prime[p++]=i,cnt[i]=1; for(int j=0;j<p&&i*prime[j]<N;j++) { iscomp[i*prime[j]]=1;cnt[i*prime[j]]=cnt[i]+1; if(i%prime[j]==0)break; } } } void build(int o,int l,int r) { mn[o]=1; if(l==r) { Id[o]=l;pos[o]=o; return; } int mid=l+r>>1; build(lc,l,mid);build(rc,mid+1,r); pos[o]=pos[lc]; } void push_down(int o,int l,int r) { if(!lazy[o])return; int mid=l+r>>1; lazy[lc]+=lazy[o];lazy[rc]+=lazy[o]; mn[lc]+=lazy[o];mn[rc]+=lazy[o]; lazy[o]=0; } void update(int o,int l,int r,int nl,int nr,int v) { if(nl<=l&&r<=nr) { mn[o]+=v;lazy[o]+=v; return; } push_down(o,l,r); int mid=l+r>>1; if(nl<=mid)update(lc,l,mid,nl,nr,v); if(nr>mid)update(rc,mid+1,r,nl,nr,v); mn[o]=min(mn[lc],mn[rc]); pos[o]=(mn[o]==mn[lc]?pos[lc]:pos[rc]); } int main() { //freopen("in.txt","r",stdin); primetable();fib[0]=1,fib[1]=1; for(int i=2;i<50;i++)fib[i]=fib[i-1]+fib[i-2]; for(int i=49;i>1;i--)fib[i]-=fib[i-1]; for(int i=1;i<=10000;i++)g[i]=1; scanf("%d",&t); for(int i=1;i<=t;i++) scanf("%d%d",&ask[i].n,&ask[i].k),ask[i].id=i; sort(ask+1,ask+t+1);int m=ask[t].n; memset(ans,-1,sizeof(ans)); build(1,1,10000); for(int i=1,mi,add,ID,id=1;id<=t&&i<=m;i++) { mi=pos[1],ID=Id[mi]; update(1,1,10000,ID,ID,0); add=mn[mi]; update(1,1,10000,1,ID,-add); if(add>1&&ID<10000) update(1,1,10000,ID+1,10000,1-add); update(1,1,10000,ID,ID,fib[++g[ID]]); while(id<=t&&ask[id].n<i+add-1)res[ask[id].id]=ans[ask[id].k],id++; i+=add-1;if(id>t||i>m)break; ans[ID]+=cnt[i]+(ans[ID]<0); while(id<=t&&ask[id].n<=i)res[ask[id].id]=ans[ask[id].k],id++; } for(int i=1;i<=t;i++)printf("%d\n",res[i]); return 0; }
總結
二麻二麻。斐波拉契數列相減那段挺好,還有那個最小值大於1的trick
C.乘積
賽後題解 講道理,中間那些公式變形看明白了,線性篩那段給我搞蒙了……h是算啥的,為啥要%(mod-1) QAQ
#include<cstdio> const int mod=19260817,N=1e6+10; int t,a,b,prime[N/10],p,d[N],h[N],f[N]; bool iscomp[N]; long long qpow(long long a,int b) { long long ret=1; for(;b;b>>=1) { if(b&1)ret=ret*a%mod; a=a*a%mod; } return ret; } void primetable() { for(int i=2;i<N;i++) { if(!iscomp[i])prime[p++]=i,d[i]=2,h[i]=i; for(int j=0;j<p&&i*prime[j]<N;j++) { iscomp[i*prime[j]]=1; if(i%prime[j]==0) { int tmp=i,cnt=2,F=1; while(tmp%prime[j]==0)tmp/=prime[j],F+=cnt,cnt++; d[i*prime[j]]=d[tmp]*cnt; h[i*prime[j]]=1ll*qpow(h[tmp],d[i/tmp*prime[j]])*qpow(prime[j],1ll*F*d[tmp]%(mod-1))%mod; break; } d[i*prime[j]]=d[i]*2; h[i*prime[j]]=1ll*h[i]*h[i]%mod*qpow(prime[j],d[i])%mod; } } } void Init() { f[0]=f[1]=h[0]=h[1]=d[1]=1;primetable(); for(int i=2;i<N;i++) { d[i]+=d[i-1]; f[i]=1ll*f[i-1]*qpow(i,d[i]%(mod-1))%mod; h[i]=1ll*h[i-1]*h[i]%mod; } for(int i=2;i<N;i++) h[i]=1ll*h[i-1]*h[i]%mod; } int main() { Init(); scanf("%d",&t); for(int i=1;i<=t;i++) { scanf("%d%d",&a,&b); printf("%d\n",1ll*qpow(f[a-1],mod-2)*f[b]%mod*qpow(h[b],mod-2)%mod*h[a-1]%mod); } return 0; }
賽後總結
生平最怕的數論專場……三場都看不懂(逃
學到的東西,最重要就是公式變形。把不好搞的部分拆成一些好搞的來搞也是種很好的trick。資料結構維護啥的也挺好。反正都挺好(我都做不起)