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;
}

posted @ 2022-03-29 18:06  Authentic_k  阅读(38)  评论(0编辑  收藏  举报