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*... ...
随机推荐
- tcl之控制流-for
- foreachRDD,foreach,foreachPartition区别联系
foreachRDD(SparkStreaming): SparkStreaming是流式实时处理数据,就是将数据流按照定义的时间进行分割(就是"批处理").每一个时间段内处理到的 ...
- win10鼠标右键菜单在左边,怎么改回右边
键盘上按WIN+R打开运行窗口,输入shell:::{80F3F1D5-FECA-45F3-BC32-752C152E456E}按回车键
- PAT (Basic Level) Practice 1040 有几个PAT
个人练习 字符串 APPAPT 中包含了两个单词 PAT,其中第一个 PAT 是第 2 位(P),第 4 位(A),第 6 位(T):第二个 PAT 是第 3 位(P),第 4 位(A),第 6 位( ...
- Git-Git库管理
对象和引用哪里去了? 从GitHub上克隆一个示例版本库,这个版本库在"历史穿梭"一章就已经克隆过一次了,现在要重新克隆一份.为了和原来的克隆相区别,克隆到另外的目录.执行下面的命 ...
- LinkedHashMap和HashMap的比较使用 详解
由于现在项目中用到了LinkedHashMap,并不是太熟悉就到网上搜了一下. import java.util.HashMap; import java.util.Iterator; import ...
- KVO的底层实现原理?如何取消系统默认的KVO并手动触发?
KVO是基于runtime机制实现的 当某个类的属性对象第一次被观察时,系统就会在运行期动态地创建该类的一个派生类(该类的子类),在这个派生类中重写基类中任何被观察属性的setter 方法.派生类在被 ...
- 《Cracking the Coding Interview》——第17章:普通题——题目9
2014-04-28 23:52 题目:设计算法,找出一本书中某个单词的出现频率. 解法:数就行了. 代码: // 17.9 Given a book, find out the occurrence ...
- appium+python的APP自动化(1)
写这个东西也是自己喜欢研究些自动化的东西,以下全是自己的经验所得,由于开源的软件对于各版本以及操作系统要求很高,会经常碰到一些不兼容的问题,这个都属于正常的,换版本就对了. 本人的环境搭建都是在win ...
- Oracle 学习---- 练习语法 循环( loop end loop; for ;while; if elsif end if )
/*--set serveroutput on;declare mynum number(3) :=0; tip varchar2(10):='结果是 ';begin mynum:=10+100; d ...