对我而言相当失败的一次考试呢。
(昨天失了智一晚上没睡觉还好意思说)
T1:
看到数据范围与值域有关,是不是想到了一些有趣的事情呢?
两两gcd为1,显然我们把同时选出的数分解质因子后,每个质数出现最多一次。是不是能状压DP呢?
(然后我发现2000内的质数有将近200个,没法状压,然后写了30分暴力)
这里有一个很显然的性质,数字x>=sqrt(x)的质因子最多只有1个,所以我们只需要状压<=sqrt(2000)的质因数,即{2-43},只有14个。
其余的,我们能按照大质因子分组,然后每组最多选1个数字。
我们令f[i][sta]表示前i组,质因数状态为sta的方案数。转移的话可以枚举这组选择哪个数字,然后进行转移。
注意没有>43质因子的数字要各自一组,因为它们不存在互斥状态。
代码:
1 #include<cstdio> 2 #include<algorithm> 3 #include<vector> 4 typedef long long int lli; 5 const int maxn=2e3+1e2,maxs=(1<<14)+5; 6 const int prime[]={2,3,5,7,11,13,17,19,23,29,31,37,41,43},pl=14,full=1<<14; 7 const int mod=1e9+7; 8 9 struct Node { 10 int sta,mx; 11 friend bool operator < (const Node &a,const Node &b) { return a.mx > b.mx; } 12 }ns[maxn]; 13 14 std::vector<Node> grp[maxn]; 15 lli f[maxn][maxs],ans; 16 int n,cnt; 17 18 inline Node cut(int x) { 19 int ret = 0; 20 for(int i=0;i<pl;i++) 21 if( ! ( x % prime[i] ) ) { 22 ret |= (1<<i); 23 while( ! ( x % prime[i] ) ) x /= prime[i]; 24 } 25 return (Node){ret,x}; 26 } 27 28 inline void getgrp() { 29 std::sort(ns+1,ns+1+n); 30 for(int i=1;i<=n;i++) { 31 if( ns[i].mx != ns[i-1].mx || ns[i].mx == 1 ) ++cnt; 32 grp[cnt].push_back(ns[i]); 33 } 34 } 35 inline void dp() { 36 **f = 1; 37 for(int i=1;i<=cnt;i++) { 38 for(unsigned cur=0;cur<grp[i].size();cur++) 39 for(int sta=0;sta<full;sta++) { 40 if( ! ( sta & grp[i][cur].sta ) ) 41 f[i][sta|grp[i][cur].sta] = ( f[i][sta|grp[i][cur].sta] + f[i-1][sta] ) % mod; 42 } 43 for(int sta=0;sta<full;sta++) f[i][sta] = ( f[i][sta] + f[i-1][sta] ) % mod; 44 } 45 for(int i=0;i<full;i++) ans = ( ans + f[cnt][i] ) % mod; 46 } 47 48 int main() { 49 scanf("%d",&n); 50 for(int i=1,t;i<=n;i++) scanf("%d",&t) , ns[i] = cut(t); 51 getgrp() , dp() , printf("%lld ",(ans-1+mod)%mod); 52 return 0; 53 }
T2:
字符串题,我只会后缀自动机......
很好,这题,后缀自动机不可做......然后我就GG了。
考虑SAM怎么做这道题,我们用可持久话线段树记录每个点的right集合,然后我们要找<=节点len的right集合中两个数的最大差......
这个东西由于存在自身对自身的贡献,所以没法启发式合并。
然后我就写了48分哈希。
考虑用后缀数组求LCP和LCS。
我们考虑枚举这个串的循环节的一半为i,然后在这个字符串中取1+k*i位置为关键点。
然后枚举两个相邻的关键点。显然如果有一个能满足条件的串存在的话,它一定是前一半过第一个关键点,后一半过第二个关键点的。
好的,我们可以求出这两个点的LCP和LCS之和,如果>=i的话,则证明存在长度>=i的满足条件的串。
复杂度为O(nlogn),注意构造后缀数组不能用SAM,会MLE.....要么DC3或者后缀平衡树,要么老老实实写倍增(然而我不会倍增)。
爆内存的SAM:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #define debug cout 6 using namespace std; 7 const int maxn=2e6+1e2,maxl=23; 8 9 char in[maxn>>1]; 10 int Log[maxn>>1],li,ans; 11 12 struct SuffixAutomatic { 13 int ch[maxn][26],tc[maxn][26],fa[maxn],len[maxn],rit[maxn],root,last,cnt; 14 int stk[maxn],sa[maxn>>1],rnk[maxn>>1],h[maxn>>1],rmq[maxn>>1][maxl],top,sal; 15 SuffixAutomatic() { last = root = cnt = 1; } 16 inline int NewNode(int ll) { 17 return len[++cnt] = ll , cnt; 18 } 19 inline void extend(int x,int r) { 20 int p = last , np = NewNode(len[p]+1); rit[np] = r; 21 while( p && !ch[p][x] ) ch[p][x] = np , p = fa[p]; 22 if( !p ) fa[np] = root; 23 else { 24 int q = ch[p][x]; 25 if( len[q] == len[p] + 1 ) fa[np] = q; 26 else { 27 int nq = NewNode(len[p]+1); 28 memcpy(ch[nq],ch[q],sizeof(ch[q])) , fa[nq] = fa[q] , fa[np] = fa[q] = nq; 29 while( p && ch[p][x] == q ) ch[p][x] = nq , p = fa[p]; 30 } 31 } 32 last = np; 33 } 34 inline void pre(int pos) { 35 for(int i=0,t;i<26;i++) if( ( t = ch[pos][i] ) && len[t] == len[pos] + 1 ) { 36 stk[++top] = i , tc[fa[t]][stk[len[t]-len[fa[t]]]] = t , pre(t) , stk[top--] = '