Topcoder SRM 603 div1題解
昨天剛打了一場codeforces。。。困死了。。。不過趕在睡前終於做完了~
話說這好像是我第一次做250-500-1000的標配耶~~~
Easy(250pts):
題目大意:有一棵樹,一共n個節點,每個節點都有一個權值,兩人A和B分別進行操作,由A先手,每人可以選擇一條邊,將它刪掉得到兩個聯通塊。遊戲不斷進行下去,最後只剩下一個節點。A希望最後的節點權值盡可能大,B希望盡可能小,求這個最後的值。數據保證n<=50。
這道題真的是博弈好題啊~(感覺放到ACM很合適啊)
我們考慮第一次A會如何選擇,有以下兩種情況:
(1)A一上來就直接劃分出一個葉子節點結束遊戲,那麽A能得到的最大值就是整棵樹所有葉子節點的權值最大值。
(2)A一上來不結束遊戲,那麽A分得的新圖中一定存在一個點使得它是原圖的葉子節點,B直接將它截取出來,那麽能得到的值一定沒有第一種情況優。
言下之意就是,把整棵樹掃一遍,枚舉出葉子節點中權值最大的一個,就是答案。
時間復雜度O(n),代碼如下:
1 #include <bits/stdc++.h> 2 using namespace std; 3 int d[57],n,ans=0; 4 class MaxMinTreeGame 5 { 6 public: 7 int findend(vector <int> edges, vector <int> costs) 8 { 9 n=costs.size(); 10 for (int i=0;i<n-1;i++) ++d[i+1],++d[edges[i]]; 11 for (int i=0;i<n;i++) 12 if (d[i]==1) ans=max(ans,costs[i]); 13 return ans; 14 } 15 };
Medium(500pts):
題目大意:給定兩個正整數n和k,求有多少對字符串(A,B)滿足A和B都是長度為n且由前k個小寫字母構成的字符串,同時存在一個字符串C(不一定長度為n)滿足A+C=C+B,這裏加號指連接符。數據保證n<=1000000000,k<=26。
我們來分析一下這個式子A+C=C+B,
考慮A是由n個字符構成的,那麽C的前n個字符構成的字符串一定是A,那麽C的n+1~2n構成的也一定是A,以此類推。
也就是說,對於C這個字符串,任意連續n個字符構成的字符串一定是A。
而A+C和C+B最末尾的n個字符串也相同,也就是說B一定是A的循環同構。
那麽問題就轉化成了,有多少對字符串(A,B)滿足A和B都是長度為n且由前k個小寫字母構成的字符串,且B為A的循環同構。
兩個字符串如果循環同構,那麽一定有一個循環節,滿足這個循環節的長度是n的約數,
對於同一個循環節,那麽對於答案的貢獻度一定是這個循環節的長度。(因為循環同構可以有循環節長度個位置)
我們假設f[i]表示長度為i個循環節個數。
於是我們有f[i]=k^i-sum(f[j]),其中j是i的約數。
所以本題我們只需要先預處理n的約數,然後統計f[i],最後直接計算答案就是可以了。
時間復雜度O(sqrt(n))(算上快速冪的話O(sqrt(n)logk)),代碼如下:
1 #include <bits/stdc++.h> 2 #define modp 1000000007 3 #define Maxm 200007 4 using namespace std; 5 int a[Maxm],f[Maxm]; 6 int cnt=0,ans=0; 7 class PairsOfStrings 8 { 9 int power(int a,int b) 10 { 11 int ans=1,left=b,now=a; 12 while (left) 13 { 14 if (left%2==1) ans=(1LL*ans*now)%modp; 15 left/=2; 16 now=(1LL*now*now)%modp; 17 } 18 return ans; 19 } 20 public: 21 int getNumber(int n, int k) 22 { 23 for (int i=1;1LL*i*i<=n;i++) 24 if (n%i==0) 25 { 26 a[++cnt]=i; 27 if (1LL*i*i!=n) a[++cnt]=n/i; 28 } 29 sort(a+1,a+cnt+1); 30 for (int i=1;i<=cnt;i++) f[i]=power(k,a[i]); 31 for (int i=1;i<=cnt;i++) 32 { 33 for (int j=1;j<i;j++) 34 if (a[i]%a[j]==0) f[i]=(f[i]+modp-f[j])%modp; 35 ans=(ans+1LL*a[i]*f[i]%modp)%modp; 36 } 37 return ans; 38 } 39 };
Hard(1000pts):
題目大意:給你兩個長度為n的隨機序列,現在可以任意交換同一個序列中的兩個數的位置,然後將兩個序列相同位置的數相加得到一個新的數列,現在要求這個數列的眾數出現次數盡可能多,如果相同,這個數盡可能大,輸出這個數和出現次數。數據滿足n<=100000,所有數<100000。
一般情況如果TC要給你一堆數,會給你一個種子,這題也不例外。
但是一般TC題會說:“本題實際可以處理所有情況。”然而這題卻沒有,所以說這個隨機就變得很重要了。
我們先O(n)進行一下統計,每個數列為i的有多少個。
接下來考慮如果直接暴力,顯然對於兩個數x和y,如果它們出現的次數是a和b,那麽對於x+y這個數出現次數的貢獻度就是min(a,b),
於是我們每一次枚舉出現次數i,
對於兩個數列,分別構造多項式,如果x在這個數列中出現了大於等於i次,那麽第x項就是1,否則就是0。
於是我們把這兩個多項式乘起來,掃一遍就可以得到答案了。
然而n的範圍有100000,顯然這樣是不行的。
這裏就要運用隨機的玄學了,由於數列是隨機的,我們可以知道出現次數超過某個數的數其實並不是很多,然後我們隨便選一個出來,比如我們選10。
我們先暴力預處理出,出現次數>10次的數,這是可以在O(cnt1*cnt2)完成的,其中cnt表示該數列出現次數超過10次的數的個數。
接下來我們一樣運用上面的方法,i從1枚舉到10,進行10次多項式乘法就可以了。
而多項式乘法,我們可以運用FFT進行,復雜度O(nlogn),
總時間復雜度O(cnt1*cnt2+10*nlogn),代碼如下:
1 #include <bits/stdc++.h> 2 #define Maxn 150007 3 int a[Maxn],b[Maxn],n; 4 int cnt1[Maxn],cnt2[Maxn]; 5 //cnt means how many times the number appears in the sequence 6 int pos1[Maxn],pos2[Maxn],tot1,tot2; 7 //pos means the value that exists often(more than ten times) in the sequence 8 long long x[2*Maxn],y[2*Maxn],z[2*Maxn]; 9 long long ans[2*Maxn]; 10 using namespace std; 11 typedef struct 12 { 13 double real,imag; 14 }com; 15 com A[Maxn*2],B[Maxn*2]; 16 class SumOfArrays 17 { 18 com com_add(com a,com b) 19 { 20 return (com){a.real+b.real,a.imag+b.imag}; 21 } 22 com com_sub(com a,com b) 23 { 24 return (com){a.real-b.real,a.imag-b.imag}; 25 } 26 com com_mul(com a,com b) 27 { 28 return (com) 29 { 30 a.real*b.real-a.imag*b.imag, 31 a.real*b.imag+a.imag*b.real 32 }; 33 } 34 void fft(com *a, int n, int flag) 35 { 36 for (int i=n/2,j=1;j<n;++j) 37 { 38 if (i<j) swap(a[i],a[j]); 39 int k=n/2; 40 while (i&k) {i^=k;k/=2;} 41 i^=k; 42 } 43 for (int k=2;k<=n;k*=2) 44 { 45 com root=(com){cos(M_PI/k*flag*2),sin(M_PI/k*flag*2)}; 46 for (int i=0;i<n;i+=k) 47 { 48 com w=(com){1.0, 0.0}; 49 for (int j=i;j<i+k/2;++j) 50 { 51 com u=a[j],v=com_mul(a[j+k/2],w); 52 a[j]=com_add(u,v); 53 a[j+k/2]=com_sub(u,v); 54 w=com_mul(w,root); 55 } 56 } 57 } 58 } 59 void multiply() 60 { 61 memset(z,0,sizeof(z)); 62 memset(A,0,sizeof(A)); 63 memset(B,0,sizeof(B)); 64 for (int i=0;i<100000;i++) A[i].real=1.0*x[i],A[i].imag=0.0; 65 for (int i=0;i<100000;i++) B[i].real=1.0*y[i],B[i].imag=0.0; 66 int len=2; 67 while (len<200000) len<<=1; 68 fft(A,len,1),fft(B,len,1); 69 for (int i=0;i<len;i++) A[i]=com_mul(A[i],B[i]); 70 fft(A,len,-1); 71 for (int i=0;i<2*100000-1;i++) 72 z[i]=(long long)trunc(A[i].real/len+0.5); 73 } 74 public: 75 string findbestpair(int N, vector <int> Aseed, vector <int> Bseed) 76 { 77 n=N; 78 a[0]=Aseed[0],a[1]=Aseed[1]; 79 for (int i=2;i<n;i++) a[i]=(1LL*a[i-1]*Aseed[2]+1LL*a[i-2]*Aseed[3]+Aseed[4])%Aseed[5]; 80 b[0]=Bseed[0],b[1]=Bseed[1]; 81 for (int i=2;i<n;i++) b[i]=(1LL*b[i-1]*Bseed[2]+1LL*b[i-2]*Bseed[3]+Bseed[4])%Bseed[5]; 82 memset(cnt1,0,sizeof(cnt1)); 83 memset(cnt2,0,sizeof(cnt2)); 84 for (int i=0;i<n;i++) ++cnt1[a[i]],++cnt2[b[i]]; 85 tot1=0; 86 for (int i=0;i<100000;i++) 87 if (cnt1[i]>10) pos1[++tot1]=i; 88 tot2=0; 89 for (int i=0;i<100000;i++) 90 if (cnt2[i]>10) pos2[++tot2]=i; 91 memset(ans,0,sizeof(ans)); 92 for (int i=1;i<=tot1;i++) 93 for (int j=1;j<=tot2;j++) 94 ans[pos1[i]+pos2[j]]+=min(cnt1[pos1[i]],cnt2[pos2[j]])-10; 95 for (int i=1;i<=10;i++) 96 { 97 memset(x,0,sizeof(x)); 98 memset(y,0,sizeof(y)); 99 for (int j=0;j<100000;j++) 100 { 101 if (cnt1[j]>=i) x[j]=1; else x[j]=0; 102 if (cnt2[j]>=i) y[j]=1; else y[j]=0; 103 } 104 multiply(); 105 for (int j=0;j<200000;j++) 106 ans[j]+=z[j]; 107 } 108 int anss=0; 109 for (int i=0;i<200000;i++) 110 if (ans[i]>=ans[anss]) anss=i; 111 char res[25]; 112 sprintf(res,"%lld %d",ans[anss],anss); 113 return res; 114 } 115 };
Topcoder SRM 603 div1題解