Topcoder SRM 603 div1题解

Posted 一只蒟篛酱的日常~

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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题解的主要内容,如果未能解决你的问题,请参考以下文章

Topcoder SRM 605 div1 题解

Topcoder SRM 608 div1 题解

Topcoder SRM 604 div1题解

Topcoder SRM 607 div1题解

TopCoder SRM502 Div1 1000 动态规划

topcoder srm 335 div1