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题解的主要内容,如果未能解决你的问题,请参考以下文章