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),代码如下:
#include <bits/stdc++.h>
using namespace std;
int d[],n,ans=;
class MaxMinTreeGame
{
public:
int findend(vector <int> edges, vector <int> costs)
{
n=costs.size();
for (int i=;i<n-;i++) ++d[i+],++d[edges[i]];
for (int i=;i<n;i++)
if (d[i]==) ans=max(ans,costs[i]);
return ans;
}
};
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)+d(n)logn)),代码如下:
#include <bits/stdc++.h>
#define modp 1000000007
#define Maxm 200007
using namespace std;
int a[Maxm],f[Maxm];
int cnt=,ans=;
class PairsOfStrings
{
int power(int a,int b)
{
int ans=,left=b,now=a;
while (left)
{
if (left%==) ans=(1LL*ans*now)%modp;
left/=;
now=(1LL*now*now)%modp;
}
return ans;
}
public:
int getNumber(int n, int k)
{
for (int i=;1LL*i*i<=n;i++)
if (n%i==)
{
a[++cnt]=i;
if (1LL*i*i!=n) a[++cnt]=n/i;
}
sort(a+,a+cnt+);
for (int i=;i<=cnt;i++) f[i]=power(k,a[i]);
for (int i=;i<=cnt;i++)
{
for (int j=;j<i;j++)
if (a[i]%a[j]==) f[i]=(f[i]+modp-f[j])%modp;
ans=(ans+1LL*a[i]*f[i]%modp)%modp;
}
return ans;
}
};
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),代码如下:
#include <bits/stdc++.h>
#define Maxn 150007
int a[Maxn],b[Maxn],n;
int cnt1[Maxn],cnt2[Maxn];
//cnt means how many times the number appears in the sequence
int pos1[Maxn],pos2[Maxn],tot1,tot2;
//pos means the value that exists often(more than ten times) in the sequence
long long x[*Maxn],y[*Maxn],z[*Maxn];
long long ans[*Maxn];
using namespace std;
typedef struct
{
double real,imag;
}com;
com A[Maxn*],B[Maxn*];
class SumOfArrays
{
com com_add(com a,com b)
{
return (com){a.real+b.real,a.imag+b.imag};
}
com com_sub(com a,com b)
{
return (com){a.real-b.real,a.imag-b.imag};
}
com com_mul(com a,com b)
{
return (com)
{
a.real*b.real-a.imag*b.imag,
a.real*b.imag+a.imag*b.real
};
}
void fft(com *a, int n, int flag)
{
for (int i=n/,j=;j<n;++j)
{
if (i<j) swap(a[i],a[j]);
int k=n/;
while (i&k) {i^=k;k/=;}
i^=k;
}
for (int k=;k<=n;k*=)
{
com root=(com){cos(M_PI/k*flag*),sin(M_PI/k*flag*)};
for (int i=;i<n;i+=k)
{
com w=(com){1.0, 0.0};
for (int j=i;j<i+k/;++j)
{
com u=a[j],v=com_mul(a[j+k/],w);
a[j]=com_add(u,v);
a[j+k/]=com_sub(u,v);
w=com_mul(w,root);
}
}
}
}
void multiply()
{
memset(z,,sizeof(z));
memset(A,,sizeof(A));
memset(B,,sizeof(B));
for (int i=;i<;i++) A[i].real=1.0*x[i],A[i].imag=0.0;
for (int i=;i<;i++) B[i].real=1.0*y[i],B[i].imag=0.0;
int len=;
while (len<) len<<=;
fft(A,len,),fft(B,len,);
for (int i=;i<len;i++) A[i]=com_mul(A[i],B[i]);
fft(A,len,-);
for (int i=;i<*-;i++)
z[i]=(long long)trunc(A[i].real/len+0.5);
}
public:
string findbestpair(int N, vector <int> Aseed, vector <int> Bseed)
{
n=N;
a[]=Aseed[],a[]=Aseed[];
for (int i=;i<n;i++) a[i]=(1LL*a[i-]*Aseed[]+1LL*a[i-]*Aseed[]+Aseed[])%Aseed[];
b[]=Bseed[],b[]=Bseed[];
for (int i=;i<n;i++) b[i]=(1LL*b[i-]*Bseed[]+1LL*b[i-]*Bseed[]+Bseed[])%Bseed[];
memset(cnt1,,sizeof(cnt1));
memset(cnt2,,sizeof(cnt2));
for (int i=;i<n;i++) ++cnt1[a[i]],++cnt2[b[i]];
tot1=;
for (int i=;i<;i++)
if (cnt1[i]>) pos1[++tot1]=i;
tot2=;
for (int i=;i<;i++)
if (cnt2[i]>) pos2[++tot2]=i;
memset(ans,,sizeof(ans));
for (int i=;i<=tot1;i++)
for (int j=;j<=tot2;j++)
ans[pos1[i]+pos2[j]]+=min(cnt1[pos1[i]],cnt2[pos2[j]])-;
for (int i=;i<=;i++)
{
memset(x,,sizeof(x));
memset(y,,sizeof(y));
for (int j=;j<;j++)
{
if (cnt1[j]>=i) x[j]=; else x[j]=;
if (cnt2[j]>=i) y[j]=; else y[j]=;
}
multiply();
for (int j=;j<;j++)
ans[j]+=z[j];
}
int anss=;
for (int i=;i<;i++)
if (ans[i]>=ans[anss]) anss=i;
char res[];
sprintf(res,"%lld %d",ans[anss],anss);
return res;
}
};
Topcoder SRM 603 div1题解的更多相关文章
- Topcoder SRM 602 div1题解
打卡- Easy(250pts): 题目大意:rating2200及以上和2200以下的颜色是不一样的(我就是属于那个颜色比较菜的),有个人初始rating为X,然后每一场比赛他的rating如果增加 ...
- Topcoder SRM 607 div1题解
好久没来写了,继续继续... Easy(250pts): //前方请注意,样例中带有zyz,高能预警... 题目大意:给你一个字符串,中间有一些是未知字符,请你求出这个字符串的回文子串个数的期望值.数 ...
- Topcoder SRM 608 div1 题解
Easy(300pts): 题目大意:有n个盒子,一共有S个苹果,每个盒子有多少个苹果不知道,但是知道每个盒子的苹果下限和上限.现在要至少选择X个苹果,问如果要保证无论如何都能获得至少X个苹果,至少需 ...
- Topcoder SRM 606 div1题解
打卡! Easy(250pts): 题目大意:一个人心中想了一个数,另一个人进行了n次猜测,每一次第一个人都会告诉他实际的数和猜测的数的差的绝对值是多少,现在告诉你所有的猜测和所有的差,要求你判断心中 ...
- Topcoder SRM 605 div1 题解
日常打卡- Easy(250pts): 题目大意:你有n种汉堡包(统统吃掉-),每一种汉堡包有一个type值和一个taste值,你现在要吃掉若干个汉堡包,使得它们taste的总和*(不同的type值的 ...
- Topcoder SRM 604 div1题解
CTSC考完跑了过来日常TC--- Easy(250pts): 题目大意:有个机器人,一开始的位置在(0,0),第k个回合可以向四个方向移动3^k的距离(不能不动),问是否可以到达(x,y),数据满足 ...
- Topcoder SRM 601 div1题解
日常TC计划- Easy(250pts): 题目大意:有n个篮子,每个篮子有若干个苹果和橘子,先任取一个正整数x,然后从每个篮子中选出x个水果,把nx个水果放在一起,输出一共有多少种不同的组成方案.其 ...
- Topcoder SRM 600 div1题解
日常TC计划正式启动! Easy(250pts): 题目大意:给你一个集合,里面一堆数,初始数为0,给你一个目标数,你可以选择集合中若干个数进行OR操作来得到目标数.问至少删去多少个数,使得你永远无法 ...
- Topcoder SRM 643 Div1 250<peter_pan>
Topcoder SRM 643 Div1 250 Problem 给一个整数N,再给一个vector<long long>v; N可以表示成若干个素数的乘积,N=p0*p1*p2*... ...
随机推荐
- JZOJ 1738. Heatwave
Description 给你N个点的无向连通图,图中有M条边,第j条边的长度为: d_j. 现在有 K个询问. 每个询问的格式是:A B,表示询问从A点走到B点的所有路径中,最长的边最小值是多少? I ...
- Ubuntu下安装libpcap+测试安装
1.从ftp://ftp.gnu.org/gnu/下载flex.bison.GNU M4.libpcap安装包,具体的链接分别如下: flex下载:http://flex.sourceforge.ne ...
- 学习python第十三天,函数5 装饰器decorator
定义:装饰器本质是函数,(装饰其他函数)就是为其他函数添加附加功能原则:1.不能修改被装饰的函数的源代码 2.不能修改装饰的函数的调用方式 实现装饰器知识储备1函数即变量2.高阶函数,满足2个条件之一 ...
- ./vi: line 2: mkdir: command not found
当前两天博主在写脚本的时候,运行脚本时候总是出现此消息,很郁闷, 开始我以为可能是我mkdir的函数库依赖的问题,但是当我用其他的脚本创建 目录的时候,命令又可以用了,找了半天,终于找到了答案 --- ...
- Linux基础知识与命令1(su passwd)
一.Linux的基本原则 1.linux由一个个目的单一的小程序组成,我们一般需要组合小程序来完成复杂的任务 2.Linux的一切都是文件(文件类似于一棵树,包括外设,接口) 3.Linux尽量避免捕 ...
- Linux平台下安装MySQL
1.下载RPM包 http://dev.mysql.com/downloads/mysql/5.5.html#downloads 选择[Red Hat & Oracle Enterprise ...
- [Bzoj1037][ZJOI2008]生日聚会(DP)
Description 题目链接 Solution 这题状态比较难想, \(dp[i][j][g][h]\)表示强i个人有j个男生,在某个区间男生最多比女生多g人,女生最多比男生多h人的方案数,然后D ...
- com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 's.areaname' in 'field list'错误
在使用mybatis框架做查询的时候,出现了如下错误: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown colum ...
- 强烈推荐android初学者,android进阶者看看这个系列教程
强烈推荐android初学者,android进阶者看看这个系列教程 转载 2015年05月30日 23:05:44 695 为什么要研究Android,是因为它够庞大,它够复杂,他激起了我作为一个程序 ...
- 剑指Offer - 九度1385 - 重建二叉树
剑指Offer - 九度1385 - 重建二叉树2013-11-23 23:53 题目描述: 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树.假设输入的前序遍历和中序遍历的结果中都不含重复的 ...