zoukankan      html  css  js  c++  java
  • 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)+d(n)logn)),代码如下:

     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 };
  • 相关阅读:
    PostgreSQL数据库((数据库类型介绍)
    postgreSQL(SQL语音操作数据库)
    postgreSQL数据库连接注意事项
    数据库
    Excel函数文本变E+显示怎么办
    无糖可乐不好喝
    通过 命令查看 linux桌面使用的是wayland 还是 X11
    Intel CPU的后缀含义
    互联网缩略语
    linux 下为windows交叉编译 ffmpeg库
  • 原文地址:https://www.cnblogs.com/Tommyr7/p/6815038.html
Copyright © 2011-2022 走看看