更好的阅读体验:https://www.cnblogs.com/lilibuxiangtle/p/13352686.html
给出c和n,求fc(n)。
递归到最后 fc 函数肯定等于1,那么就变成了求c被乘了几次,只要找到 x 最多能被分解成多少个数相乘就好了。预处理用线性筛求出每个数最多能被分解成多少个数相乘,快速幂求出解。
1 #include
2 #define ll long long
3 #define pb push_back
4 #define ft first
5 #define sd second
6 #define pii pair
7 #define pll pair
8 using namespace std;
9
10 ll st[1001000];
11 ll prime[1001000];
12 ll d[1001000];
13 ll cnt;
14 const ll mod=1e9+7;
15 void Prime(ll n)
16 {
17 cnt=0;
18 for(ll i=2;i<=n;i++){
19 if(!st[i]) prime[cnt++]=i,d[i]=1;
20 for(int j=0;prime[j]*i<=n;j++){
21 st[prime[j]*i]=1;
22 d[prime[j]*i]=d[i]+1;
23 if(i%prime[j]==0) break;
24 }
25 }
26 }
27 ll quick(ll a,ll b)
28 {
29 ll res=1;
30 a=a%mod;
31 while(b){
32 if(b&1) res=(res*a)%mod;
33 a=(a*a)%mod;
34 b>>=1;
35 }
36 return res%mod;
37 }
38 int main()
39 {
40 ios::sync_with_stdio(false);
41 cin.tie(0);
42 cout.tie(0);
43 Prime(1000000);
44 for(ll i=1;i<=10;i++) cout<>t;
47 while(t--){
48 ll n,c;
49 cin>>n>>c;
50 cout<
f(S,x,y)(1≤x≤y≤n)表示一个长度为y-x+1的字符串,字符串的第 i 位为maxi=x...x+k−1Si。 A={ f( f(S,x1,y1), x2−x1+1 , y2−x1+1 ) | 1≤x1≤x2≤y2≤y1≤n},求有多少不同的集合A。
出题人的题解:
• 核心点 1
• 这题等价于 f(S,i,n) 这 n 个串的不同子串的个数
• 核心点 2
• 假设当前字符的位置是 i,最近的大于等于它的字符的位置
是 j,那么新增的代价是 j-i。
• f(S,i,n) 翻转后构成的字典树的大小不超过 10N
• 所以我们要考虑这个字典树本质不同的子串
• 最暴力的方法:在构成的字典树上建广义后缀自动机即可,
设 k 为字符集大小,复杂度O(Nk2)
• 也可以用一些哈希或序列自动机的方法求一求。
我的想法:
比赛的时候第一点我就没看出来,好菜.....
A集合中区间范围可以看出[x2,y2]完全包含在[x1,y1]里边,所以[x2,y2]这个是不起任何作用的。故问题就转换成了求f(S,x,y) (1\leq x\leq y\leq n)f(S,x,y)(1≤x≤y≤n)有多少本质不同的子串。这个问题不就是广义后缀自动机的经典题型了吗。但是如果对于所有的[x1,y1]都建立sam的话,肯定是不行的,于是就要用到别的办法。观察可以发现f(S,x,y)f(S,x,y)调用过程中任意一个位置的字符最多会被替换9次(因为子集字符串长度最大为10)。看巨巨们的博客有两种方法,一种用单调队列完成,一种是序列自动机。找到第一个大于等于s[i]的字符的位置记录为pos,那么只要每次往sam中插入s[i]~s[pos-1]即可。
关于序列自动机的做法:(来自@Frozen_Guardian )nt[ i ][ j ] 表示为记录位置 i 开始的首次大于等于字母 j 的位置,这样对于第 i 个位置的字母 j 来说,只需要暴力更新一下 [ i , nt[ i ][ j ] - 1 ] 就好了,nt[ i ][ j ] 往后的位置都可以直接从前面维护的 trie 树中拉下来继续用。
序列自动机+SAM
#include
#define ll long long
using namespace std;
const int maxn=1e6+10;
struct state
{
int mxlen,link;
int cnt;
int nt[26];
}st[maxn<<1];
int sz,last,len;
char s[maxn];
inline void sam_init()
{
st[0].mxlen=0;
st[0].link=-1;
sz=1;
last=0;
}
inline void sam_extend(int c)
{
int cur=sz++;
st[cur].mxlen=st[last].mxlen+1;
st[cur].cnt=1;
int p=last;
while(p!=-1&&!st[p].nt[c]){
st[p].nt[c]=cur;
p=st[p].link;
}
if(p==-1) st[cur].link=0;
else{
int q=st[p].nt[c];
if(st[p].mxlen+1==st[q].mxlen) st[cur].link=q;
else{
int clone=sz++;
st[clone].mxlen=st[p].mxlen+1;
memcpy(st[clone].nt,st[q].nt,sizeof(st[q].nt));
st[clone].link=st[q].link;
while(p!=-1&&st[p].nt[c]==q){
st[p].nt[c]=clone;
p=st[p].link;
}
st[q].link=st[cur].link=clone;
}
}
last=cur;
}
int nt[maxn][10],id[maxn];
//nx[i][j]第i个位置(包括)后首次出现大于等于j的位置
int main()
{
cin>>s+1;
sam_init();
int n=strlen(s+1);
for(int i=0;i<10;i++) nt[n+1][i]=n+1;
for(int i=n;i>=1;i--){
for(int j=0;j<10;j++) nt[i][j]=nt[i+1][j];
nt[i][s[i]-'a']=i;
for(int j=8;j>=0;j--) nt[i][j]=min(nt[i][j],nt[i][j+1]);
}
id[n+1]=last;
for(int i=n;i>=1;i--){
int pos=nt[i+1][s[i]-'a'];
last=id[pos];
for(int j=i;j
单调队列+SAM
#include
#define ll long long
using namespace std;
const int maxn=1e6+10;
struct state
{
int mxlen,link;
int cnt;
int nt[26];
}st[maxn<<1];
int sz,last,len;
char s[maxn];
inline void sam_init()
{
st[0].mxlen=0;
st[0].link=-1;
sz=1;
last=0;
}
inline void sam_extend(int c)
{
int cur=sz++;
st[cur].mxlen=st[last].mxlen+1;
st[cur].cnt=1;
int p=last;
while(p!=-1&&!st[p].nt[c]){
st[p].nt[c]=cur;
p=st[p].link;
}
if(p==-1) st[cur].link=0;
else{
int q=st[p].nt[c];
if(st[p].mxlen+1==st[q].mxlen) st[cur].link=q;
else{
int clone=sz++;
st[clone].mxlen=st[p].mxlen+1;
memcpy(st[clone].nt,st[q].nt,sizeof(st[q].nt));
st[clone].link=st[q].link;
while(p!=-1&&st[p].nt[c]==q){
st[p].nt[c]=clone;
p=st[p].link;
}
st[q].link=st[cur].link=clone;
}
}
last=cur;
}
int id[maxn];
int main()
{
cin>>s+1;
sam_init();
int n=strlen(s+1);
stackss;
ss.push(n+1);
id[n+1]=last;
for(int i=n;i>=1;i--){
while(ss.size()!=1&&s[ss.top()]
有两条平行的线AB和CD,给出AC, AD, BC, BD 的长度,分别为a, b, c, d。问是AB//CD,还是AB//DC。
菜鸡本人的代码
1 #include
2 #define ll long long
3 #define pb push_back
4 #define ft first
5 #define sd second
6 #define pii pair
7 #define pll pair
8 using namespace std;
9
10 int main()
11 {
12 ios::sync_with_stdio(false);
13 cin.tie(0);
14 cout.tie(0);
15 int t;
16 cin>>t;
17 while(t--){
18 int a,b,c,d;
19 cin>>a>>b>>c>>d;
20 if(b>a&&c>d&&b>d||a>b&&c>d&&c>a||b>a&&d>c&&b>d||a==b&&c>d&&c>a||c==d&&b>a&&b>d) cout<<"AB//CD"<
巨巨的代码
1 #include
2 using namespace std;
3 typedef long long ll;
4 const int N=1e3+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
5 #define mst(a) memset(a,0,sizeof a)
6 #define lx x<<1
7 #define rx x<<1|1
8 #define reg register
9 #define PII pair
10 #define fi first
11 #define se second
12 #define pb push_back
13 int main(){
14 int t;
15 cin>>t;
16 while(t--){
17 int a,b,c,d;
18 cin>>a>>b>>c>>d;
19 puts(b+c>a+d?"AB//CD":"AB//DC");
20 }
21 return 0;
22 }
集合A和B都是{1,2,.....,n}的子集,A∩B≠∅。问A和B最多有多少对数GCD(Ap,Bq)>1。
所有gcd>1的两个数肯定是倍数关系,最小肯定是本身和2倍。越大的数能和他匹配的就会越少,为了简单,从大的数往小的数匹配。先用线性筛O(n)预处理出1-n之间的质数,然后找到小于n的质数最大,并且要有至少一个数可以和他配对。从这个质数开始往下计算,如果有偶数个可以和该质数配对的,那就直接记录下来;如果是奇数个,最简单的方法是将这个数的二倍放弃,剩下的记录下来。因为是2的倍数,肯定存在,并且方便计算。
1 #include
2 #define ll long long
3 #define pb push_back
4 #define ft first
5 #define sd second
6 #define pii pair
7 #define pll pair
8 using namespace std;
9
10 ll st[200100];
11 ll prime[200100];
12 ll cnt;
13
14 void Prime(ll n)
15 {
16 cnt=0;
17 for(ll i=2;i<=n;i++){
18 if(!st[i]) prime[cnt++]=i;
19 for(int j=0;prime[j]*i<=n;j++){
20 st[prime[j]*i]=1;
21 if(i%prime[j]==0) break;
22 }
23 }
24 }
25
26 ll quick(int a,int b)
27 {
28 int res=1;
29 while(b){
30 if(b&1) res=(res*a);
31 a=(a*a);
32 b>>=1;
33 }
34 return res;
35 }
36
37 bool vis[200100];
38 vectorv[200100];
39
40 int main()
41 {
42 ios::sync_with_stdio(false);
43 cin.tie(0);
44 cout.tie(0);
45 int t;
46 cin>>t;
47 Prime(200000);
48 while(t--){
49 memset(vis,0,sizeof(vis));
50 int n;
51 cin>>n;
52 for(int i=0;i<=n;i++) v[i].clear();
53 int k=cnt-1;
54 for(int i=0;in) {k=i-1;break;}
56 }
57 int ans=0;
58 for(int i=k;i>=0;i--){
59 int p=n/prime[i],x=0;
60 for(int j=1;j<=n/prime[i];j++)
61 if(!vis[prime[i]*j]) x++;
62 if(x&1){
63 int flag=0;
64 if(n/prime[i]<2) continue;
65 for(int j=1;j<=n/prime[i];j++){
66 if(!flag&&!vis[prime[i]*j]&&(prime[i]*j)%2==0) flag=1; //删掉一个2的倍数
67 else if(!vis[prime[i]*j]) {
68 vis[prime[i]*j]=1;
69 v[i].push_back(prime[i]*j);
70 }
71 }
72 }
73 else{
74 for(int j=1;j<=n/prime[i];j++)
75 if(!vis[prime[i]*j]) {
76 vis[prime[i]*j]=1;
77 v[i].push_back(prime[i]*j);
78 }
79 }
80 }
81 for(int i=0;i<=k;i++){
82 ans+=v[i].size()/2;
83 }
84 cout<