在比賽開始30分鍾內依次A了10,7,3,一個小時左右A了11,然后就再也沒有寫出任何一道題。。。
其實比賽過程中心態有很大的問題,我一直在5上面鑽牛角尖,耗費了太多時間。
基本上比賽后半程三個人都在看2,5兩道題,其他題目幾乎沒看。
到最后基本沒有做下去的欲望了,可能是因為是女隊壓力比較小吧(不,是因為我比較菜)。。
以及網絡賽的排行榜真的很迷。。第2題min25篩一堆人過。。然而隊里三個人甚至都沒有聽說過這玩意。。
(226大佬7題,ORZ)
1002 Graph Theory Class
題意:在一個有n個點的圖中,圖中點1~n編號。對於i,j之間的一條邊,它的權重為lcm(i+1,j+1),求這個圖的最小生成樹大小。
思路:很明顯對於(j+1)為質數的情況,j應該與1連接,權重為2*(j+1);
對於(j+1)不是質數的情況,j可以與任意一個滿足i+1==factor(j+1)的i連接,factor(j+1)指j+1的因子,權重為j+1。
於是最小生成樹的大小為(3~n+1以內所有質數)*2+(3~n+1以內所有非合數),即(3~n+1所有數之和)+(3~n+1以內所有質數之和)
前者可以用等差數列求和計算,(n+4)*(n-1)/2;由於n<=1010,后者可以使用min25篩計算。
min25篩,可以在O(n3/4/logn)的時間內求積性函數f(x)的前綴和。
(積性函數,指對於所有互質的整數a和b有性質f(ab)=f(a)f(b)的函數)
(代碼待補充
1003 Express Mail Taking
題意:有n個保險櫃,編號為1~n,每兩個相鄰編號的保險櫃之間的距離為1,第k個保險櫃用來打開其他保險櫃(每次只能同時打開一個保險櫃)。從1出發,需要從m個保險櫃中取東西后再從1離開。問距離最短的路線長度。
思路:一開始理解錯了題意,以為只要到達第k保險櫃后其他保險櫃就打開了,就WA了一次。
對於m個要取東西的櫃子,除了最后一個櫃子有可能不需要返回k櫃,對於其他櫃子,都必須在該櫃子與k櫃之間走一個來回。
於是找到m個櫃子中最小的一個x,若x比k小,則將該點刪去(在回1點的路上就可到達,對距離無貢獻)
接着對剩下的每個櫃子i,貢獻2*abs(i-k)的答案。
再加上出發和結束的2*(k-1),就是答案。

#include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; const int maxn=2000005; int a[maxn]; int main() { int i,T,n,m,k,mi; ll ans; scanf("%d",&T); while (T--) { scanf("%d%d%d",&n,&m,&k); ans=2ll*(k-1); mi=k; for (i=1;i<=m;i++) { scanf("%d",&a[i]); ans+=2ll*abs(a[i]-k); if (a[i]<mi) mi=a[i]; } if (mi!=k) ans-=2ll*abs(mi-k); printf("%lld\n",ans); } return 0; }
1005 Lunch
題意:有n個數,兩人輪流進行操作,每次操作能將其中一個數x變為k*(x/k),此處的k必須是x的因子且k!=x。當場上的數均為1時游戲結束,當前操作方輸。問先手可不可以獲勝。
思路:(看了半天別人的題解還是有點暈乎乎的)
觀察發現,對於一個數,分成偶數個會增加偶數個操作,於是這樣不會使得最后的勝負情況發生改變。
分成奇數個則剛好相反,會改變最后的勝負情況。
把數x分解,x就變成了一堆質因子的乘積。
()
因為每次操作要讓這個數x變成自己的因數,所以每次必須取走x的至少一個質因子,
那么就可以將這個數的所有質因子(考慮重復,因為假如你有2個質因子3,那么是可以分開取,所以重復的而要考慮)當作一堆石子的個數。
而其中的2它比較特殊,因為它是偶數,不會使最后的勝負情況發生改變。
於是不管分幾次取2,都相當於直接把2全取走。但是又必須要取走2,所以就把所有的因數2當作一個石子一起取走。
(以上來自隊友的分析)
然后就變成了nim博弈,即有n堆石子,兩人輪流進行操作,每次可從任意一堆石子里取走任意多枚石子,不能不取,問先手必勝還是必敗。
對於答案,求每一堆的石子數的異或和,若為0則必敗,否則必勝。
(然后不知道為什么T了很久,好像也不是讀入優化的問題啊。。。
補充:對於nim答案的分析:對於一堆石子,只要石子數不為0,直接拿走所有石子就贏了,所以必勝。
對於兩堆石子,如果異或和為0則必敗。此時兩堆石子數相同,對方只要按照先手方對一堆石子的操作,對另一堆石子上進行同樣的操作,就能夠勝利。
如果異或和不為0則必勝。先手方可以先將一堆石子變為和另一堆石子一樣,然后就變成了剛才所說的局面,異或和為0。
......
對於很多堆石子,如果異或和為0必敗;否則改變其中最大的那堆的大小,一定能使剩下的局面的異或和為0。
(局面異或和為0的具體操作:對手對一堆進行操作,其他堆中一定有某一堆,可以使先手對它做出相同的操作。)

#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int cnt,vis[32005],prime[5005]; int div(int x) { int re=0; while (!(x&1)) { x>>=1; re=1; } for (int i=2;i<=cnt && 1ll*prime[i]*prime[i]<=x;i++) { while (x%prime[i]==0) { x=x/prime[i]; re++; } } if (x!=1) re++; return re; } void pri(int n) { cnt=0; memset(vis,0,sizeof(vis)); for (int i=2;i<n;i++) { if (!vis[i]) prime[++cnt]=i; for (int j=1;j<=cnt && 1ll*i*prime[j]<n;j++) { vis[i*prime[j]]=1; if (i%prime[j]==0) break; } } } int main() { int T,n,ans,x; pri(32000); scanf("%d",&T); while (T--) { scanf("%d",&n); ans=0; for (int i=0;i<n;i++) { scanf("%d",&x); ans^=div(x); } if (ans) printf("W\n"); else printf("L\n"); } return 0; }
1006 Robotic Class
題意:給一個有n點的圖,點i有k[i]條出邊,若通過i的一條出邊j,就會使當前的權值x變為c[i][j]*x+b[i][j],並走到下一點d[i][j]。對於有多條出邊的點i,每次會選擇一條滿足a[i][j]≥x並且編號j最小的路徑j。現在從任一點s出發,初始權值為x0,走到n點停止,最后到達n點的權值為Cs(x0),問對於每一個s,Cs(x)是否均為連續。
思路:看完了別人的代碼,感覺這題沒做出來有點虧。。
因為f(x)連續,所以若在i點的權值為a[i][j]-與a[i][j]+,那么兩者到達n點得到的權值應當相同。
所以對於每一個點i的a[i][j],模擬以初始權值x0=a[i][j]-與a[i][j]+從i出發直到n的路徑,檢查最后得到的Ci(a[i][j]-)與Ci(a[i][j]+)是否相等。
具體見代碼。

#include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; const int maxn=505; const int maxm=2005; int n,k[maxn],a[maxn][maxm],b[maxn][maxm],c[maxn][maxm],d[maxn][maxm]; ll dfs(int t,ll x,int mark) { if (t==n) return x; int pos=lower_bound(a[t],a[t]+k[t],x)-a[t]; if (pos<k[t] && x==a[t][pos] && mark>0) pos++; int now=0; if (c[t][pos]>0) now=1; else if (c[t][pos]<0) now=-1; return dfs(d[t][pos],1ll*c[t][pos]*x+b[t][pos],now*mark); } int main() { int t,T,i,j,flag; ll l,r; scanf("%d",&T); for (t=1;t<=T;t++) { scanf("%d",&n); for (i=1;i<n;i++) { scanf("%d",&k[i]); for (j=0;j<k[i];j++) scanf("%d%d%d%d",&c[i][j],&b[i][j],&d[i][j],&a[i][j]); scanf("%d%d%d",&c[i][j],&b[i][j],&d[i][j]); } flag=1; for (i=1;i<n;i++) { for (j=0;j<k[i];j++) { l=dfs(i,a[i][j],-1); r=dfs(i,a[i][j],1); if (l!=r) { flag=0; break; } } if (!flag) break; } printf("Case #%d: ",t); if (flag) printf("YES\n"); else printf("NO\n"); } return 0; }
1007 CCPC Training Class
題意:花里胡哨的題面。
思路:是一題題面比較長的簽到題。。

#include<cstdio> #include<algorithm> #include<cstring> using namespace std; int t,a[30],mx,pos,cs,l; char s[100010]; int main() { scanf("%d",&t); while (t--) { scanf("%s",s+1); l=strlen(s+1); mx=0; memset(a,0,sizeof(a)); for (int i=1;i<=l;i++) a[s[i]-'a']++; for (int i=0;i<26;i++) mx=max(mx,a[i]); printf("Case #%d: %d\n",++cs,mx); } return 0; }
1010 Reports
題意:就是每次輸入一個長度為n的01串,若存在相鄰兩位一樣就輸出NO,否則輸出YES。
思路:這么短的題目顯然是簽到題啦,照題意做就行了。

#include<cstdio> using namespace std; int t,y,x,op,n; int main() { scanf("%d",&t); while (t--) { scanf("%d",&n); scanf("%d",&y); op=1; for (int i=1;i<n;i++) { scanf("%d",&x); if (x==y) op=0; y=x; } if (!op) puts("NO"); else puts("YES"); } return 0; }
1011 3x3 Convolution
題意:給出n*n矩陣A和3*3矩陣K(n>=3), 定義n*n矩陣C(A,K),它的每一項Cx,y滿足
定義Cm(A,K)=C(Cm−1(A,K),K) and C1(A,K)=C(A,K),求limt→∞Ct(A,K)。
思路:觀察樣例,發現樣例中答案只有兩種,原矩陣和零矩陣。
經過一番胡亂分析,推測當輸入的K矩陣只有左上角有不為0的數,其他位置都是0時,輸出原矩陣;其他情況輸出零矩陣,
然后跟隊友說了猜想,隊友就A了,A得我一頭霧水,果然像學長說的一樣,大膽猜想,不用證明(bushi)。
以及這題的輸出格式真的。。。我寫的時候PE了一片,(然而我的隊友一發就過了。。
補充:C1,1=A1,1*K1,1+A1,2*K1,2+......+A3,2*K3,2+A3,3*K3,3
......
Cn,n-1=An,n-1*K1,1+An,n*K1,2
Cn,n=An,n*K1,1
然后進行分類討論
1、如果K1,1==0,Cn,n就為0;接下來的C2中的Cn,n-1也會變成0,以此類推,最后矩陣Ct會變成零矩陣。
2、如果K1,1!=0,K其他位都為0,那么矩陣中的所有項相當於乘上了K1,1的t次方,各項之間的比值不變,於是答案是原矩陣。
3、如果K1,1!=0,K其他位中存在不為0的項,因為K1,1<1,Cn,n會不斷變小,直至無限趨近於0
推理可得,Ct中的Cn,n-1=An,n-1*Kt1,1+t*An,n*Kt-11,1*K1,2,
由於K所有項之和為1,K1,1與K1,2都小於1,分析可得當t→∞時,Ct中的該項趨近於0;
同理,其他項在Ct中也都趨近於0,於是最后得到的Ct是零矩陣。

#include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; const int maxn=52; int a[maxn][maxn]; int main() { int i,j,T,n,flag,x; scanf("%d",&T); while (T--) { scanf("%d",&n); for (i=1;i<=n;i++) for (j=1;j<=n;j++) scanf("%d",&a[i][j]); flag=0; for (i=1;i<=3;i++) for (j=1;j<=3;j++) { scanf("%d",&x); if (x) { if (i==1 && j==1) flag=1; else flag=0; } } if (flag) { for (i=1;i<=n;i++) for (j=1;j<=n;j++) { printf("%d",a[i][j]); if (j==n) printf("\n"); else printf(" "); } } else { for (i=1;i<=n;i++) for (j=1;j<=n;j++) { printf("0"); if (j==n) printf("\n"); else printf(" "); } } } return 0; }

#include<cstdio> #define N 55 using namespace std; int t,n,a[N][N],b[N][N],sum; int main() { scanf("%d",&t); while (t--) { scanf("%d",&n); for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) scanf("%d",&a[i][j]); sum=0; for (int i=1;i<=3;i++) for (int j=1;j<=3;j++) scanf("%d",&b[i][j]),sum+=b[i][j]; if (b[1][1]==sum) for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) printf("%d%c",a[i][j]," \n"[j==n]); else for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) printf("0%c"," \n"[j==n]); } return 0; }