3.29省选模拟
数学场
改完题之后,我的测试用时是最短的(今天是难得的小常数的一天~)
快了两秒~
不,三秒~
\(T1\)
\(DP\)一开始看上去确实不是很显然
开始以为是个贪心,不过确实带有贪心的思想
考虑最后答案数列
\(q_1,q_2,q_3/ q_4\)假设\(G=gcd(q_1,q_2,q_3),\)那么\(q_4\)不可能是\(G\)的倍数,否则的话,放到前面到了这一步\(G\)不变,而且贡献可以增加
考虑设计\(dp\)状态
\(dp_i\)表示我们我们前缀\(\gcd\)序列结尾为\(i\)的\(\gcd\)最大值
考虑转移,我们说了,新的\(\gcd\)不能是倍数,那么新的一段肯定是一个因数
转移易得
\(dp_i=dp_j+(Num_i-Num_j)\times i\)
意思是,我们原来\(\gcd\)为\(j,\)现在\(\gcd\)为\(i\)我们只需要加入,是\(i\)的倍数不是\(j\)倍数的数字就好了
而且我们枚举\(i\)时候,只需要枚举\(i\)的素数倍数,因为非素数倍数的贡献已经在素数里面了
确实没有想到状态竟然是\(\gcd,\)还是需要多见一下
\(\huge NO \ define \ int\ \ long \ long\)
具体详情请看我犇犇
#define Eternal_Battle ZXK
#include<bits/stdc++.h>
#define MAXN 20000000
using namespace std;
int Num[MAXN+5],pri[MAXN+5],a[MAXN+5],Maxn,cnt,n;
int vis[MAXN+5];
long long dp[MAXN],Ans;
void Init(int Maxn)
{
for(int i=2;i<=Maxn;i++)
{
if(!vis[i]) pri[++cnt]=i,vis[i]=i;
for(int j=1;j<=cnt&&i*pri[j]<=Maxn&&pri[j]<=vis[i];j++)
{
vis[i*pri[j]]=pri[j];
if(i%pri[j]==0) break;
}
}
}
signed main()
{
scanf("%lld",&n);
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
Maxn=max(Maxn,a[i]);
dp[a[i]]+=a[i];
}
Init(Maxn);
for(int i=1;i<=n;i++)
{
for(int j=1;j*j<=a[i];j++)
{
if(a[i]%j==0)
{
Num[j]++;
if(j*j!=a[i])
{
Num[a[i]/j]++;
}
}
}
}
for(int i=Maxn;i>=1;i--)
{
for(int j=1;j<=cnt&&i*pri[j]<=Maxn;j++)
{
dp[i]=max(dp[i],dp[i*pri[j]]+1ll*(Num[i]-Num[i*pri[j]])*i);
}
Ans=max(Ans,dp[i]);
}
cout<<Ans<<"\n";
}
\(T2\)
简单\(PN\)筛,最起码\(Min\underline{}25\)和\(PN\)都得会吧
定义了一个新奇的函数\(σ^*(n)\)表示\(n\)的元因子之和,元因子\(d|n,\gcd(d,\frac{n}{d})=1\)
通俗的说,这个因子只出现了一次
至于\(PN\)筛,贴一个链接(原来写的博客)就好了
https://www.cnblogs.com/Eternal-Battle/p/15856794.html
\(Powerful \ Number\) 筛比筛\(Min\underline{}25\)简单是怎么回事呢\(?\)\(Powerful \ Number\)r 筛相信大家都很熟悉\(,\)但是\(Powerful \ Number\) 筛比\(Min\underline{}25\)筛简单是怎么回事呢\(,\)下面就让小编带大家一起了解吧\(.\)
\(Powerful \ Number\) 筛比\(Min\underline{}25\)筛简单\(,\)其实就是比Min_25筛更加好理解\(,\)大家可能会很惊讶\(Powerful \ Number\)筛怎么会比\(Min\underline{}25\)筛简单呢\(?\)但事实就是这样\(,\)小编也感到非常惊讶\(.\)
这就是关于\(Powerful \ Number\) 筛比\(Min\underline{}25\)筛简单的事情了\(,\)大家有什么想法呢\(,\)欢迎在评论区告诉小编一起讨论哦\(!\)
还是考虑\(PN\)的过程
构造函数\(g\)在质数位置等于\(σ^*\)
这个貌似不用构造,\(σ\)是个天然的函数
我们需要有快速求\(G\)的方法
那么\(G(n)=\sum_{i=1}^nσ(i)=\sum_{d=1}^nd\lfloor\frac{n}{d}\rfloor\)
快速求\(h=f/g\)的方法,打表\(...\)(打表很重要)发现我们\(h(p^k)=-p(k=2)\),其余都是\(0\)
那么就套一套式子就好啦
\(\sum_{i=1}^nf(i)=\sum_{i=1}^n(h*g)(i)\)
\(=\sum_{i=1}^n\sum_{d|i}h(d)g(\frac{i}{d})\)
\(=\sum_{d=1}^n h(d)\sum_{i|d}g(\frac{i}{d})\)
\(=\sum_{d=1}^n h(d)\sum_{i=1}^{n/d}g(i)\)
\(=\sum_{d=1}^n h(d)G(\frac{n}{d})\)
自然的,\(d\)是\(PN\)啦
#define Eternal_Battle ZXK
#include<bits/stdc++.h>
#define ull unsigned long long
#define MAXN 10000000
#define ll long long
using namespace std;
ll pri[MAXN+5],Num[MAXN+5],Sum[MAXN],sd[MAXN+5],cnt,n;
bool vis[MAXN+5];
void Init()
{
Num[1]=sd[1]=1;
for(int i=2;i<=MAXN;i++)
{
if(!vis[i])
{
pri[++cnt]=i;
Num[i]=i+1;
sd[i]=i+1;
}
for(int j=1;j<=cnt&&i*pri[j]<=MAXN;j++)
{
vis[i*pri[j]]=1;
if(i%pri[j]==0)
{
Num[i*pri[j]]=Num[i]*pri[j]+1;
sd[i*pri[j]]=sd[i]/Num[i]*Num[i*pri[j]];
break;
}
Num[i*pri[j]]=pri[j]+1;
sd[i*pri[j]]=sd[i]*(pri[j]+1);
}
}
for(int i=1;i<=MAXN;i++)
{
Sum[i]=Sum[i-1]+sd[i];
}
}
ull C(ll n)
{
if(n%2==0) return n/2ll*(n+1ll);
else return (n+1ll)/2ll*n;
}
ull sol(ll n)
{
if(n<=MAXN) return Sum[n];
ull res=0;
for(ll l=1,r;l<=n;l=r+1)
{
r=n/(n/l);
res+=(n/l)*(C(r)-C(l-1));
}
return res;
}
ull dfs(ll n,ll m,ll val)
{
ull res=val*sol(n);
for(int j=m+1;j<=cnt&&pri[j]*pri[j]<=n;j++)
{
res+=dfs(n/pri[j]/pri[j],j,-val*pri[j]);
}
return res;
}
int main()
{
Init();
scanf("%lld",&n);
cout<<dfs(n,0ll,1ll)<<"\n";
}
\(T3\)
找出最大的排列使得相邻两个的\(LCP\)长度的平方和最大,在保证平方和最大的前提下,贡献最大
稍加分析可以发现,我们最优的排列,必然是前缀相同的一段,那么前缀向同的一段按大小顺序排成一列就很优了,我们尽可能连续起来,那么全都按字典序排好就好了
那么答案也很好求了,按照(任意顺序即可)字典序遍历一遍字典树,计算每一个\(LCA\)的深度,平方求和即可
这个\(2^i\)让我想到了一次外出培训,就是选一个比后面全选更优,那么就如果能修正序列就修正即可
我们只要保证是一个\(dfs\)序即可,那么对一一个限制我们先加进去,然后表示我们遍历\(a,\)立马遍历\(b,\)要求了
首先较为显然的,一个点的不同子树访问任意,就是得保证是个\(dfs\)序,我们要保证相连的话,
那么一个最后访问,一个第一个访问就好了,然后打上标记,至于什么时候不合法,就是本来是最后一个,又是第一个就直接跳过
#define Eternal_Battle ZXK
#include<bits/stdc++.h>
#define ll long long
#define MAXN 100010
using namespace std;
inline int rd(){
int r = 0 , w = 1;
char ch = getchar();
while(ch > '9' || ch < '0') {if(ch == '-') w = -1; ch = getchar();}
while(ch >='0' && ch <='9') {r = r * 10 + ch -'0'; ch = getchar();}
return r * w;
}
struct EB
{
int to,nxt;
};
EB ed[MAXN<<1];
int head[MAXN<<1],tot1=-1;
void add(int x,int y)
{
ed[++tot1].to=y;
ed[tot1].nxt=head[x];
head[x]=tot1;
}
int dep[MAXN<<1],fa[MAXN<<1],siz[MAXN<<1];
struct List
{
int Pre,Beg,End,Len;
};
List lst[MAXN<<1];
int tr[MAXN<<1][27],Trcnt;
int Insert(char *s)
{
int now=0;
int L=strlen(s);
for(int i=0;i<L;i++)
{
int ch=s[i]-'a'+1;
if(tr[now][ch]==0)tr[now][ch]=++Trcnt,dep[tr[now][ch]]=dep[now]+1;
now=tr[now][ch];
}
tr[now][0]=++Trcnt;
dep[tr[now][0]]=dep[now]+1;
return tr[now][0];
}
ll dfs(int x,int f)
{
lst[x].Beg=x;lst[x].End=x;lst[x].Len=1;
fa[x]=f;
int cnt=0;
for(int i=0;i<27;i++)
{
if(tr[x][i]==0) continue;
siz[x]++;
}
if(x&&(f==0||siz[x]!=1)) add(f,x);
ll res=0;
for(int i=0;i<27;i++)
{
int to=tr[x][i];
if(to==0) continue;
if(f==0||siz[x]!=1) res+=dfs(to,x);
else res+=dfs(to,f);
if(cnt) res+=1ll*dep[x]*dep[x];
cnt++;
}
return res;
}
int LCA(int x,int y)
{
while(x!=y)
{
if(dep[x]>dep[y]) x=fa[x];
else y=fa[y];
}
return x;
}
int Back[MAXN<<1],Front[MAXN<<1],Back2[MAXN<<1],Front2[MAXN<<1];
bool check(int x,int y)
{
int lca=LCA(x,y);
int nowx,nowy,t;
t=x;
while(t!=lca)
{
int f=fa[t];
if(f!=lca)
{
if(Back[f]&&Back[f]!=t) return 0;
if(lst[t].Beg==Front[f]&&lst[lst[t].Beg].Len<siz[f]) return 0;
if(Back2[t]) return 0;
}
nowx=t;
t=f;
}
t=y;
while(t!=lca)
{
int f=fa[t];
if(f!=lca)
{
if(Front[f]&&Front[f]!=t) return 0;
if(lst[t].End==Back[f]&&lst[t].Len<siz[f]) return 0;
if(Front2[t]) return 0;
}
nowy=t;
t=f;
}
if(Back2[nowx]==nowy) return 1;
if(Back[lca]==nowx||Front[lca]==nowy) return 0;
if(Back2[nowx]) return 0;
if(Front2[nowy]) return 0;
if(lst[nowx].Beg==lst[nowy].Beg) return 0;
if(lst[nowx].Beg==Front[lca]&&lst[nowy].End==Back[lca]&&lst[lst[nowx].Beg].Len+lst[nowy].Len<siz[lca]) return 0;
Back2[nowx]=nowy;
Front2[nowy]=nowx;
lst[lst[nowy].End].Beg=lst[nowx].Beg;
lst[lst[nowx].Beg].End=lst[nowy].End;
lst[lst[nowx].Beg].Len+=lst[nowy].Len;
t=x;
while(fa[t]!=lca)
{
Back[fa[t]]=t;
t=fa[t];
}
t=y;
while(fa[t]!=lca)
{
Front[fa[t]]=t;
t=fa[t];
}
return 1;
}
int tot;
void Print(int x)
{
if(siz[x]==0)
{
cout<<lst[x].Pre<<" ";
tot--;
return ;
}
if(Front[x])
{
int t=Front[x];
while(t)
{
Print(t);
t=Back2[t];
}
}
for(int i=head[x];i!=-1;i=ed[i].nxt)
{
int to=ed[i].to;
if(lst[to].Beg==Front[x]||lst[to].End==Back[x]||Front2[to]) continue;
int t=lst[to].Beg;
while(t)
{
Print(t);
t=Back2[t];
}
}
if(Back[x])
{
int t=lst[Back[x]].Beg;
while(t)
{
Print(t);
t=Back2[t];
}
}
}
struct Combo
{
int x,y;
};
Combo Q[MAXN];
int id[MAXN<<1],ans[MAXN],h,n,q;
char str[MAXN];
namespace Subtask1
{
void main()
{
cout<<dfs(0,0)<<"\n";
}
}
namespace Subtask23
{
void main()
{
for(int i=1;i<=q;i++) Q[i].x=rd(),Q[i].y=rd();
for(int i=q;i>=1;i--)
{
if(check(id[Q[i].x],id[Q[i].y]))
{
ans[++h]=i;
}
}
cout<<h;
for(int i=h;i;i--) cout<<" "<<ans[i];
cout<<"\n";
tot=n;
Print(0);
}
}
signed main()
{
freopen("sakuya.in","r",stdin);
freopen("sakuya.out","w",stdout);
n=rd();
q=rd();
memset(head,-1,sizeof(head));
for(int i=1;i<=n;i++)
{
scanf("%s",str);
id[i]=Insert(str);
lst[id[i]].Pre=i;
}
Subtask1::main();
Subtask23::main();
return 0;
}