首页 > 代码库 > Topcoder SRM 603 div1题解

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题解