省選模擬賽記錄
LOG 模擬賽
第一次見尼瑪這麽給數據範圍的……
開考有點困,迷迷糊糊看完了三道題,真的是像老呂說的那樣,一道都不會……
思考T1,感覺有點感覺,但是太困了,就先碼了暴力,發現打表可以50分,於是就大力打了一波表……轉身T3,碼出25分的O(n^2)算法,然後不會了……去了T2,碼出35分的O(n^2)bfs,然後不會了……+
考試還有1h+,我又看了一遍三道題,發現並沒有什麽新的思路,於是決定去想T1,繼續考試一開始的思路——我發現每加一位,一定是在原合法方案的基礎上加的.想到這裏,我就迅速碼出,發現需要寫尼瑪高精度,然而考試還剩下不到10min……我估了一下我現在這題的分——70分,於是決定不打高精度了……
後來發現我的T1做法基本就是正解了,只要再寫上一個漂亮的高精度就可以A掉了……而正解呢,只不過是繼續把性質推了一下然後把我的刷表法變成了填表法……
T3正解是,在我的思路的基礎之上,利用題目給出的隨機樹的條件,把我每次都要重新處理的東西記錄了下來,這樣像動態點分治一樣每次查詢加修改就可以了……
思維筆記:I.信息共用以減少處理次數 II.利用隨機
算法筆記:I.隨機樹(我們假設其為有根樹)樹高期望log(實際上都快到根號級別了) II.隨機樹(我們假設其為有根樹)中,每個點的子樹樹高與其在整棵樹中的深度的乘積的加和的期望是n^1.5的
雀巢咖啡系列模擬賽 XLI
異化多肽(T1):
看到這個東西還有那個模數就像要去ntt一發,然後開始推式子.
推啊推,推啊推,推出來一個等比數列求和的式子,很開心,感覺自己要A題了.
然後快速碼出快速冪ntt+多項式求逆,調完之後,極限數據5s!!!感覺藥丸,然後一波卡常,3s左右,就沒再管……
最後90,T了一個點,發現一個很蠢蛋的問題——我湊,我快速冪ntt個蛋啊,mdzz,我要快速冪ntt求的那個多項式在mod x^(n+1)意義下是0啊!!!!感覺自己蠢到家了……
所以這題只不過是一個比較裸的多項式求逆,我為什麽要快速冪ntt呢?應該就是我多項式這兒太菜了,當然我覺得也有考試的時候腦子不太正常的原因,還有我的極限思想應該是不太好.
#pragma GCC optimize("O3") #include <cstdio> #include <cstring> #include <algorithm> typedef long long LL; const int N=100010; const int P=1005060097; inline int Pow(int x,int y){ int ret=1; while(y){ if(y&1)ret=(LL)ret*x%P; x=(LL)x*x%P,y>>=1; }return ret; } int A[N<<2],ai[N<<2],rev[N<<2],inv[N<<2],len; int n,m; inline void ntt(int *C,int opt){ register int i,j,k,w;int wn,temp; for(i=1;i<len;++i)if(rev[i]>i)std::swap(C[i],C[rev[i]]); for(k=2;k<=len;k<<=1){ wn=Pow(5,(P-1)/k); if(opt==-1)wn=Pow(wn,P-2); for(i=0;i<len;i+=k){ w=1; for(j=0;j<(k>>1);++j,w=(LL)w*wn%P){ temp=(LL)w*C[i+j+(k>>1)]%P; C[i+j+(k>>1)]=(C[i+j]-temp+P)%P; C[i+j]=(C[i+j]+temp)%P; } } } } inline void get_inv(int *a,int *b,int cd){ if(cd==1){b[0]=Pow(a[0],P-2);return;} get_inv(a,b,cd>>1),len=cd<<1; int i,ni=Pow(len,P-2); for(i=1;i<len;++i)rev[i]=(rev[i>>1]>>1)|((i&1)?(len>>1):0); memcpy(A,a,cd<<2),memset(A+cd,0,cd<<2); ntt(A,1),ntt(b,1); for(i=0;i<len;++i)b[i]=(2-(LL)b[i]*A[i]%P+P)%P*(LL)b[i]%P; ntt(b,-1); for(i=0;i<cd;++i)b[i]=(LL)b[i]*ni%P; memset(b+cd,0,cd<<2); } int main(){ scanf("%d%d",&n,&m);int i,x; for(i=1;i<=m;++i) scanf("%d",&x),++ai[x]; ai[0]=P-1,len=1; while(len<=n)len<<=1; get_inv(ai,inv,len); printf("%d\n",(P-inv[n])%P); return 0; }View Code
編碼病毒(T2):
讀懂題之後,哇塞,時限有4s,完全可以O(n^2)再來一個1/32的常數用bitset水之啊,然後15min搞好……
最後100,發現正解是比bitset還要水的水fft,就是一個變了一下形的bzoj2194,但是bitset比這玩意好寫到不知道哪裏去了……
#include <cstdio> #include <bitset> #include <cstring> #include <algorithm> const int N=100010; std::bitset<N> test,data,temp; int n,m,cost[N],cnt[(1<<22)+10]; char s[N]; int main(){ scanf("%d%d",&m,&n); int full=(1<<m)-1,i,j,ans=0x3f3f3f3f; memset(cost,0x3f,sizeof(cost)),cost[0]=0; for(i=1;i<=full;++i){ cnt[i]=cnt[i-((-i)&i)]+1; cost[i%n]=std::min(cost[i%n],cnt[i]+1); cost[((-i)%n+n)%n]=std::min(cost[((-i)%n+n)%n],cnt[i]); } for(scanf("%s",s),i=0;i<n;++i)data[i]=s[i]==‘1‘; for(scanf("%s",s),i=0;i<n;++i)test[i]=s[i]==‘1‘; for(i=0;i<n;++i){ if(i!=0)j=data[0],data>>=1,data[n-1]=j; temp=data^test; j=temp.count(),j=std::min(j,n-j+1); ans=std::min(ans,j+cost[i]); } printf("%d\n",ans); return 0; }Bitset
#include <cmath> #include <cstdio> #include <cstring> #include <complex> #include <algorithm> typedef double db; typedef std::complex<db> cd; const int N=300010; const db Pi=std::acos(-1.); cd A[N<<2],B[N<<2]; int test[N<<2],data[N<<2],ci[N<<2],rev[N<<2],len; int n,m,cost[N],cnt[(1<<22)+10]; char s[N]; inline void fft(cd *C,int opt){ register int i,j,k;cd temp; for(i=1;i<len;++i) if(rev[i]>i) std::swap(C[rev[i]],C[i]); for(k=2;k<=len;k<<=1){ cd wn(std::cos(2*opt*Pi/k),std::sin(2*opt*Pi/k)); for(i=0;i<len;i+=k){ cd w(1.,0.); for(j=0;j<(k>>1);++j,w*=wn){ temp=w*C[i+j+(k>>1)]; C[i+j+(k>>1)]=C[i+j]-temp; C[i+j]+=temp; } } } } inline void mul(int *a,int *b,int *c,int n){ len=1;while(len<n)len<<=1;int i; for(i=0;i<len;++i){ rev[i]=(rev[i>>1]>>1)|((i&1)?(len>>1):0); A[i]=a[i],B[i]=b[i]; } fft(A,1),fft(B,1); for(i=0;i<len;++i)A[i]*=B[i]; fft(A,-1); db inv=1./len; for(i=0;i<len;++i)c[i]=round(A[i].real()*inv); } int main(){ scanf("%d%d",&m,&n); int full=(1<<m)-1,i,j,ans=0x3f3f3f3f; memset(cost,0x3f,sizeof(cost)),cost[0]=0; for(i=1;i<=full;++i){ cnt[i]=cnt[i-((-i)&i)]+1; cost[i%n]=std::min(cost[i%n],cnt[i]+1); cost[((-i)%n+n)%n]=std::min(cost[((-i)%n+n)%n],cnt[i]); } for(scanf("%s",s),i=0;i<n;++i)data[i]=data[i+n]=s[i]==‘1‘?1:-1; for(scanf("%s",s),i=0;i<n;++i)test[n-i-1]=s[i]==‘1‘?1:-1; mul(test,data,ci,n+n+n); for(i=0;i<n;++i){ j=(ci[i+n-1]+n)>>1; ans=std::min(ans,cost[i]+std::min(j+1,n-j)); } printf("%d\n",ans); return 0; }fft
數論函數簇(T3):
考場上從看這道題到最後,我對這道題的解法基本沒有變——暴力模擬題意……
最後20分……
看到題解發現這題一道神題……
首先,你在暴力模擬題意的時候,會發現,滿足a*a=a(mod n),a*b=0(mod n)的a和b就是滿足條件的a和b,然後繼續觀察,發現滿足a*a=a(mod n)的a會對答案貢獻gcd(a,n).
然後,我們會發現我們的瓶頸在於找到滿足a*a=a(mod n)的a,我們可以把這個式子寫成a*(a-1)=0(mod n),發現gcd(a,a-1)=1,這個時候我們可以改變對a要滿足的條件的表述——因為a與a-1互質,所以gcd(a,n)與gcd(a-1,n)互質,所以若a滿足條件,當且僅當gcd(a,n)與gcd(a-1,n)的乘積為n,也就是說a與a-1中含有不相交且加起來是完整的n的n的兩部分.
接著,我們設q為gcd(a-1,n),p為gcd(a,n),並且先假設1<=a<=n,那麽a-1從0一直到n-1讓q取到了所有的n的因數,我們先把gcd(q,n/q)!=1的q全部舍去,那麽剩下了幾種q,對於每種q,若想找到一個存在其對應的p的a,就是找到一個含有其對應的p的a模q余1,也就是找到一個k,使得kp%q=1,而k的取值為[1,q],所以對於每一種合法q,都會找到一個互不相同的a,從而做出其對應的p的貢獻.也就是說,如果我們放在p的角度上觀察,就是對於每一個p,只要gcd(p,n/p)=1,就會做出p的貢獻.至此,我們找到了一個新的計算R值的方法:R[n]=∑(d|n)[gcd(d,n/d)=1]*d.那麽R[n]就相當於n的所有滿足另一半與自己互質的約數和,利用這個我們顯然可以用歐拉篩法O(n)得到所有的R,這樣我們就得到了50分.
接下來,我們利用我們剛剛提到的式子進行一波反演,大力推一波式子,最後推出來的式子是要1到n^0.5枚舉一個值並且每次對n除這個值的平方向下取整得到的數進行除法分塊,據說這樣的復雜度是O(n^0.5logn),反正可以過……
這道題對於數論推導能力,數論裏許多性質的掌握以及化式子的能力考察得很好,是一道很好的題.
#include <cstdio> #include <cstring> #include <algorithm> typedef long long LL; const int N=1000000,P=1005060097,inv2=502530049; #define mod(a) ((a)<P?(a):(a)%P) char mu[N+10]; int prime[N+10],len; bool isnot[N+10]; inline LL calc(LL lim){ LL ret=0; for(register LL i=1,last=1;i<=lim;i=last+1){ last=lim/(lim/i); ret=(ret+mod(i+last)*mod(last-i+1)%P*inv2%P*mod(lim/i)%P)%P; } return ret; } int main(){ register int i,j,k; mu[1]=1; for(i=2;i<=N;++i){ if(!isnot[i])prime[++len]=i,mu[i]=-1; for(j=1;prime[j]*i<=N;++j){ isnot[prime[j]*i]=true; if(i%prime[j]==0){mu[prime[j]*i]=0;break;} mu[prime[j]*i]=-mu[i]; } } LL n;int ans=0; scanf("%lld",&n); for(k=1;(LL)k*k<=n;++k) ans=(ans+mu[k]*k*calc(n/((LL)k*k))%P+P)%P; ans=(ans-(n%P)*((n+1)%P)%P*inv2%P+P)%P; printf("%d\n",ans); return 0; }真·正確的代碼
No.11省選模擬測試
T1:
有一小點點小思想,就是一個簡單的樹狀數組,就是用差分模擬一下模意義下的區間加和.
T2:
滿腦子dp……
又是一道網絡流思想的貪心,我好像一碰到什麽和網絡流有關的東西就不會……
只要把費用流建圖想清楚,然後用線段樹或者堆或者直接sort模擬一下就可以了……
網絡流急需加強……我的貪心也好弱啊……
T3:
我寫了一個題解上說的60分做法,然而利用矩陣裏大部分都是空的,加一下矩陣乘法的優化就過了,還挺快.這種做法就是在原矩陣中加入記錄前綴和的部分,在圖的模型中就是引入黑洞點.
正解是利用折半的方法求等比數列,這個思想很不錯,十分值得積累與借鑒.
這個折半和fft那個折半大異小同.
省選模擬賽記錄