昨天刚打了一场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题解的更多相关文章

  1. Topcoder SRM 602 div1题解

    打卡- Easy(250pts): 题目大意:rating2200及以上和2200以下的颜色是不一样的(我就是属于那个颜色比较菜的),有个人初始rating为X,然后每一场比赛他的rating如果增加 ...

  2. Topcoder SRM 607 div1题解

    好久没来写了,继续继续... Easy(250pts): //前方请注意,样例中带有zyz,高能预警... 题目大意:给你一个字符串,中间有一些是未知字符,请你求出这个字符串的回文子串个数的期望值.数 ...

  3. Topcoder SRM 608 div1 题解

    Easy(300pts): 题目大意:有n个盒子,一共有S个苹果,每个盒子有多少个苹果不知道,但是知道每个盒子的苹果下限和上限.现在要至少选择X个苹果,问如果要保证无论如何都能获得至少X个苹果,至少需 ...

  4. Topcoder SRM 606 div1题解

    打卡! Easy(250pts): 题目大意:一个人心中想了一个数,另一个人进行了n次猜测,每一次第一个人都会告诉他实际的数和猜测的数的差的绝对值是多少,现在告诉你所有的猜测和所有的差,要求你判断心中 ...

  5. Topcoder SRM 605 div1 题解

    日常打卡- Easy(250pts): 题目大意:你有n种汉堡包(统统吃掉-),每一种汉堡包有一个type值和一个taste值,你现在要吃掉若干个汉堡包,使得它们taste的总和*(不同的type值的 ...

  6. Topcoder SRM 604 div1题解

    CTSC考完跑了过来日常TC--- Easy(250pts): 题目大意:有个机器人,一开始的位置在(0,0),第k个回合可以向四个方向移动3^k的距离(不能不动),问是否可以到达(x,y),数据满足 ...

  7. Topcoder SRM 601 div1题解

    日常TC计划- Easy(250pts): 题目大意:有n个篮子,每个篮子有若干个苹果和橘子,先任取一个正整数x,然后从每个篮子中选出x个水果,把nx个水果放在一起,输出一共有多少种不同的组成方案.其 ...

  8. Topcoder SRM 600 div1题解

    日常TC计划正式启动! Easy(250pts): 题目大意:给你一个集合,里面一堆数,初始数为0,给你一个目标数,你可以选择集合中若干个数进行OR操作来得到目标数.问至少删去多少个数,使得你永远无法 ...

  9. Topcoder SRM 643 Div1 250<peter_pan>

    Topcoder SRM 643 Div1 250 Problem 给一个整数N,再给一个vector<long long>v; N可以表示成若干个素数的乘积,N=p0*p1*p2*... ...

随机推荐

  1. c#常用数据结构解析【转载】

    引用:http://blog.csdn.net/suifcd/article/details/42869341 前言:可能去过小匹夫博客的盆油们读过这篇对于数据结构的总结,但是小匹夫当时写那篇文章的时 ...

  2. Python变量、赋值及作用域

    ## 变量 - 指向唯一内存地址的一个名字 - 目的是为了更方便地引用内存中的值 - 可以使用id(变量)函数来查看变量的唯一id值,若两者id值相同,则表示两个变量指向同一地址,两个变量的值完全相同 ...

  3. 722. Remove Comments

    class Solution { public: vector<string> removeComments(vector<string>& source) { vec ...

  4. linux无名管道

    特点 无名管道是半双工的,也就是说,一个管道要么只能读,要么只能写 只能在有共同祖先的进程间使用(父子进程.兄弟进程.子孙进程等) fork或者execve调用创建的子进程,继承了父进程的文件描述符 ...

  5. VUE前端无法启动

    cd 到client中,使用npm run dev ,一直卡着也不报错,启动不了项目 可以直接使用 ,需要进入root目录进行 cnpm install npm -g

  6. fromkeys语法/set集合/深浅拷贝/列表/字典的删除

    fromkeys语法: dic = {"apple":"苹果", "banana":"香蕉"} 返回新字典. 和原来的没 ...

  7. 关于IOS下日期格式分隔符 - 、 /的问题

    之前我们项目有一个低价日历,服务端下发的时间格式为: "2014-07-21 09:45:12"  然后一直出不了数据,后来发现. IOS下无论chrome.safari还是Uc如 ...

  8. centos6安装openfire(mysql)

    一.安装JDK 我的系统是centos6.8x64,首先安装jdk1.7.0 二.安装openfire 我装的包是:openfire-3.9.3-1.i386.rpm,复制到/opt目录 #rpm - ...

  9. .net 匿名方法

    匿名方法 核心就是lambda语法,下面是使用举例: var conn= MySqlHelper.GetConn(); var list=conn.Query<User>("SE ...

  10. dpkg.cfg