1. 程式人生 > >Topcoder SRM 603 div1題解

Topcoder SRM 603 div1題解

大於 並不是 roo swa ndb mod 我們 連接 字母

昨天剛打了一場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題解