1123模拟赛(神T)

事实证明在学校的效率高得多啊。

1123 模拟赛

A Sequence

题意

\(~~~~\) 定义一个数列区间的权值 \(f(l,r)\)\(\text{mex}(a_l,\gcd(a_l,a_{l+1}),\dots,\gcd(a_l,a_{l+1},\dots,a_r))\) ,其中 \(\text{mex(S)}\) 表示集合 \(S\) 中未出现的最小整数,求所有区间的权值和。
\(~~~~\) \(1\leq n\leq 5\times 10^6.1\leq a_i\leq 5\times 10^6\).

题解

\(~~~~\) 我们来试着找一下区间权值的性质,注意到对一个数连续取 \(\text{gcd}\) 至少会 \(\div2\),所以发现 \(1,2,3\) 不会连续取得,所以 \(f\) 的值域只能是 \(\{1,2,3\}\).

\(~~~~\) 那所以我们只需要关注一个点后面哪些区间取到 \(1,2,3\)。自然地,权值取 \(1\) 的右端点 \(r\) 必然满足 \(\gcd(a_l,a_{l+1},\dots,a_r)\not=1\),我们对每个 \(l\) 维护满足这样条件的最大的 \(r\).

\(~~~~\) 然后考虑 \(2\)\(3\) ,注意到这两个权值对于同一个左端点不会同时出现,所权值非 \(1\) 部分的权值只可能是 \(2,3\) ,我们只需要求它到底是多少。

\(~~~~\) 显然当之前 \(\gcd\) 出现过 \(2\) 时权值为 \(3\) ,具体怎么维护呢?

\(~~~~\) 计数在所有 \(\gcd\)\(1\) 的区间的数中 \(2\) 的因子个数的情况,分类讨论:

  • 若这些数中存在一个数没有因子 \(2\) ,则 \(\gcd\) 中必然不存在 \(2\).
  • 否则,若存在恰好有一个因子 \(2\) 的数,那只需要关注所有数除去一个因子 \(2\) 后最远的 \(\gcd\)\(1\) 的位置之后那个数。(也就是说前面一段 \(\gcd\) 都是 \(2\) 的倍数且 \(> 2\),到这个数刚好 \(\gcd\) 不为 \(2\) 的倍数或 \(\leq 2\))那个数恰好有一个因子 \(2\) 说明是刚好为 \(2\) 的情况,权值为 \(3\) ,否则就是 \(\gcd\) 不为 \(2\) 了,那权值为 \(2\)
  • 最后,若全都有因子 \(2\) 但全都不止一个,那就找下一个有 \(0\)\(1\)\(2\) 因子的数。那个数的 \(2\)\(0\) 权值就是 \(2\),否则权值为 \(3\)

\(~~~~\) 现在的问题就是:怎么维护距离某个数最远的 \(\gcd\)\(1\) 的位置。对每个质因子和 \(1\) 考虑,维护当前段的含该质因子的数的最右位置,那最终的答案一定在那个位置之后,对所有情况取 \(\max\) 即可。当然维护去除 \(2\) 的情况就是不考虑 \(1\)

代码

查看代码
#include <bits/stdc++.h>
#define ll long long
#define PII pair<int,int>
#define mp(a,b) make_pair(a,b)
using namespace std;
static char buf[1000000],*p1=buf,*p2=buf,obuf[1000000],*p3=obuf;
#define getchar() p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++
#define putchar(x) (p3-obuf<1000000)?(*p3++=x):(fwrite(obuf,p3-obuf,1,stdout),p3=obuf,*p3++=x)
template<typename item> inline void read(register item &x){
    x=0;register int f=1;register char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
    x*=f;
}
bool vis[5000005];
int Prime[5000005],Min[5000005],Nxt[5000005],tot,Fac2[5000005],Pre0[5000005],Pre1[5000005];
int arr[5000005],To1[5000005],To2[5000005],Val[5000005],nxt[5000005];//����gcd��Ϊ1����ȥһ��2����gcd��Ϊ1λ�� 
void Init(int N)
{
	Min[1]=1;
	for(int i=2;i<=N;i++)
	{
		if(!vis[i]) Prime[++tot]=i,Min[i]=tot,Nxt[i]=1;
		for(int j=1;j<=tot&&i*Prime[j]<=N;j++)
		{
			vis[i*Prime[j]]=true; Min[i*Prime[j]]=Min[i];
			if(Prime[Min[i]]==Prime[j]) Nxt[i*Prime[j]]=Nxt[i];
			else Nxt[i*Prime[j]]=Nxt[i]*Prime[j];
			if(i%Prime[j]==0) break;
		}
	}
}
int FPos[5000005],FVal[5000005];
PII buc[5000005];
int main(){
	freopen("sequence.in","r",stdin);
	freopen("sequence.out","w",stdout);
	int n;read(n);
	for(int i=1;i<=n;i++) read(arr[i]),To1[i]=To2[i]=i-1;
	Init(5000000);
	for(int i=n;i>=1;i--)
	{
		int Tmp=arr[i];
		while(Tmp>1)
		{
			int Fac=Min[Tmp];
			if(FPos[Fac]==i+1) FPos[Fac]=i;
			else FPos[Fac]=FVal[Fac]=i;
			if(Fac==1) To1[i]=max(To1[i],FVal[Fac]);
			else
			{
				To1[i]=max(To1[i],FVal[Fac]);
				To2[i]=max(To2[i],FVal[Fac]);
			}
			Tmp=Nxt[Tmp];
		}
		Tmp=arr[i];
		while((Tmp&1)==0) Tmp>>=1,Fac2[i]++;
	}
	for(int i=1;i<=n;i++) Pre0[i]+=Pre0[i-1]+(Fac2[i]==0);
	for(int i=1;i<=n;i++) Pre1[i]+=Pre1[i-1]+(Fac2[i]==1);
	nxt[n+1]=n+1;
	for(int i=n;i>=1;i--)
	{
		if(Fac2[i]==0||Fac2[i]==1) nxt[i]=i;
		else nxt[i]=nxt[i+1];
	}
	for(int i=1;i<=n;i++)
	{
		int R=To2[i];
		int cnt0=Pre0[R]-Pre0[i-1];
		int cnt1=Pre1[R]-Pre1[i-1];
		if(!cnt0)
		{
			if(cnt1)
			{
				if(Fac2[To2[i]+1]) Val[i]=3;
				else Val[i]=2;	
			}
			else
			{
				if(Fac2[nxt[To2[i]+1]]==1) Val[i]=3;
				else Val[i]=2;
			}
		}
		else Val[i]=2;
	}
	ll Ans=0;
	for(int i=1;i<=n;i++) Ans+=(To1[i]-i+1)+1ll*(n-(To1[i]+1)+1)*Val[i];
	printf("%lld",Ans);
	fwrite(obuf,p3-obuf,1,stdout);
	return 0;
}
/*
����Ȼһ�������fֻ������1 2 3�е�һ�� 
��Ϊ����gcd������ 1 2 3 ͬʱ����
��������ֻ��Ҫ���ǵ�ǰ��˵㵽��Щ�� 1 ��Щ�� 2 ��Щ�� 3
Ҳ�������ֻ��Ҫ��עgcdΪ 1 ,2 ������ֵ�� 
ά�����뵱ǰ��Զ�� gcd��Ϊ 1 ��λ�ã�����֮��� mex ���� 1 
��ά��ÿ������ȥ������ 2 ����Զ�� gcd ��Ϊ 1 ��λ�ã���֮�� gcd �� 2
���2��1���Ǵ��ڵģ��Ǻ��沿�� mex �� 3
���ֻ��1�Ǵ��ڵģ��Ǻ��沿�� mex �� 2 
�����û�к��沿����һ���� 
���Դ�����⣿���� 

ά����Զզ�����أ�������ݷ�Χ�ƺ���̫�� n log n(ST��ά������gcd+Binary Search)�����ֺ����Dz���
Ŷ����gcd�ĸ��ӶȰ� 
�����ΰ�

��¼ÿ������С���Ǹ������ӣ���Ӧ�ÿ�����һ��xxs������� ��
Ȼ���ÿ�������ӿ�Ͱ������Ͱ��ά����һ������������Ǿ�����һ�εĵ�һ�� 
��Ȼ���ÿ���1 
����ȥ�� 

ϣ����������ˮ 
*/
	

B lantern

\(~~~~\) 未加强版本见 CF1728G 的题解区。

\(~~~~\) 加强版:\(1\leq L\leq 10^8,1\leq n\leq 5\times 10^5,m\leq 500,q\leq 5\times 10^5\).

\(~~~~\) 然后我没写加强版 /hanx

C CF1149D Abandoning Roads

题意

\(~~~~\) \(n\) 个点 \(m\) 条边的无向连通图,有 \(a,b(a<b)\) 两种边权中的一种。求从 \(1\)\(n\) 的最短路径长度,满足该路径存在与一个原图的最小生成树上。
\(~~~~\) \(1\leq n\leq 75\)

题解

\(~~~~\) 发现用克鲁斯卡尔的想法,一定会先考虑加入所有 \(a\),那么只考虑这些边就会产生若干连通块,并且这些连通块内部的任何路径一定都是合法的。

\(~~~~\) 然后考虑 \(b\) 的作用,\(b\) 是用来连接这些连通块的,因此连通块内部的 \(b\) 不能走。同时我们发现一个连通块内部不管要跨越多少 \(a\) ,它都不能用 \(b\) 走出去再走回来,因为若钦定走出去和回来的两条 \(b\) 边在生成树内,那它们必定会连成环,除此之外都是合法的。

\(~~~~\) 那我们就有了一个合理的想法:\(dp_{S,i}\) 表示已经离开了 \(S\) 内所有的连通块,现在在 \(i\) 点,那我们用类似与最短路的方式转移即可。

\(~~~~\) 然而 \(n\leq 75\),所以这还过不了。我们发现如果一个连通块的大小在 \(3\) 以内,那走遍这个连通块内部最多 \(2a\),但是出去再回来至少 \(2b\),所以直接跑最短路这个部分也是对的,dp只需要做 \(\geq 4\) 的连通块,所以只有 \(\lfloor \frac{n}{4} \rfloor\) 个连通块,现在做状压就很合适了。

\(~~~~\) 需要注意优先标号需要dp的连通块。

代码

查看代码
#include <bits/stdc++.h>
#define PII pair<int,int>
#define mp(a,b) make_pair(a,b)
using namespace std;
template<typename T>void read(T &x)
{
    T f=1;x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9') {x=x*10+s-'0';s=getchar();}
    x*=f;
}
bool vis[80];
int n,m,a,b,tot,cnt=0,col[80],Type[80],Num[80];
vector <PII> G[80];
void dfs1(int u)
{
    vis[u]=true; cnt++;
    for(int i=0;i<(int)G[u].size();i++)
    {
        int v=G[u][i].first,w=G[u][i].second;
        if(vis[v]||w!=a) continue;
        dfs1(v);
    }
}
void dfs2(int u,int Col,int T)
{
    col[u]=Col; Type[u]=T;
    for(int i=0;i<(int)G[u].size();i++)
    {
        int v=G[u][i].first,w=G[u][i].second;
        if(col[v]||w!=a) continue;
        dfs2(v,Col,T);
    }
}
struct cmp{
    bool operator()(const pair<PII,int> x,const pair<PII,int> y){return x.second>y.second;}
};
priority_queue<pair<PII,int>,vector< pair<PII,int> >,cmp>Q;
int dp[524300][80];
int main() {
    // #ifndef ONLINE_JUDGE
    freopen("pink.in","r",stdin);
    freopen("pink.out","w",stdout);
    // #endif
    read(n);read(m);read(a);read(b);
    for(int i=1,u,v,w;i<=m;i++)
    {
        read(u);read(v);read(w);
        G[u].push_back(mp(v,w));
        G[v].push_back(mp(u,w));
    }
    for(int i=1;i<=n;i++)
    {
        if(!vis[i])
        {
            dfs1(i);
            if(cnt>=4) dfs2(i,++tot,cnt>=4);
            cnt=0;
        }
    }
    for(int i=1;i<=n;i++)
    {
        if(!col[i])
        {
            dfs1(i);
            if(cnt<4) dfs2(i,++tot,cnt>=4);
            cnt=0;
        }
    }
    memset(dp,0x3f,sizeof(dp));
    Q.push(mp(mp(0,1),dp[0][1]=0));
    while(!Q.empty())
    {
        pair<PII,int>P=Q.top();Q.pop();
        int u=P.first.second,S=P.first.first,d=P.second;
        if(dp[S][u]<d) continue;
        for(int i=0;i<(int)G[u].size();i++)
        {
            int v=G[u][i].first,w=G[u][i].second;
            if(col[u]==col[v]&&w!=a) continue;
            if(Type[v]&&(S&(1<<col[v]))) continue;
            int New=S;
            if(Type[u]&&col[u]!=col[v]) New|=(1<<col[u]);
            if(dp[New][v]>d+w) dp[New][v]=d+w,Q.push(mp(mp(New,v),dp[New][v]));
        }
    }
    for(int i=2;i<=n;i++)
    {
        int res=2e9;
        for(int j=0;j<(1<<19);j++) res=min(res,dp[j][i]);
        printf("%d ",res);
    }
    return 0;
}
/*
清夜无尘。月色如银。酒斟时、须满十分。浮名浮利,虚苦劳神。叹隙中驹,石中火,梦中身。
虽抱文章,开口谁亲。且陶陶、乐尽天真。几时归去,作个闲人。对一张琴,一壶酒,一溪云。


我愿将这天地揽入怀中
寄这一生于浮生美梦
这本无什么对错 就坚守下去 恐惧终将消散
我会竭尽岁月去追逐某人的背影
描摹未来的形状
这颗心脏会在晨曦之时铿锵
当暴风骤雨席卷于此
我会变得更加顽强
*/

D CF1067D Computer Game

\(~~~~\) T 老师搬的题不必全球霸榜 rank 1 好多了?所以我成为不了全球霸榜 rank1。

题意

\(~~~~\) \(n\) 个物品,有属性 \(a_i,b_i,p_i\)(\(a_i<b_i\)),初始时每个物品价值为 \(a_i\)\(t\) 时刻,每个时刻可以选择一个物品,以 \(p_i\) 的概率获得其价值并选任意一个物品使得其价值变为 \(b_i\)
\(~~~~\) \(1\leq n\leq 10^5,1\leq T\leq 10^{10}\).

题解

\(~~~~\) 注意到如果我们有了一次升级的机会,那我们会选哪个?肯定是选 \(b_i\times p_i\) 最大的那个,下面记作 \(v\)。在那之前怎么选呢?我们dp来求解。

\(~~~~\) 假设 \(dp_t\) 表示还剩 \(t\) 时刻,那枚举我们这次选 \(i\) ,那就是:

\[\max_{i=1}^n \{ p_i[(t-1)\times v+a_i]+(1-p)\times f_{t-1} \} \]

\(~~~~\) 于是我们 \(\mathcal{O(nT)}\) 做了。

\(~~~~\) 来换一下形式:

\[\max_{i=1}^n \{ p_i[(t-1)v-f_{t-1}]+p_ia_i \}+f_{t-1} \]

\(~~~~\) 哇塞,这是不是又是一个直线形式。把 \(p_i\) 视作斜率 \(k\),\(p_ia_i\) 视作截距 \(b\)\((t-1)v-f_{t-1}\) 视作自变量 \(x\) ,所以每个物品可以视作一条直线,那我们就考虑维护下凸壳,这样我们就可以二分用哪一条直线转移,但还是会做 \(T\)次。

\(~~~~\) 进一步地,我们发现 \(tv-f_t\) 单调不降,假如有 \(tv-f_t \geq (t-1)v-f_{t-1}\) ,得到 \(v \geq f_t-f_{t-1}\) ,显然是成立的,所以单调不降是合理的。

\(~~~~\) 那么我们就可以对每个直线挨个来,能转移就转移。至于 \(t\) 的范围就可以用矩阵倍增转移直线了。

代码

查看代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
template<typename T>void read(T &x)
{
    T f=1;x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9') {x=x*10+s-'0';s=getchar();}
    x*=f;
}
struct Matrix{
    long double a[5][5];
    Matrix(){memset(a,0,sizeof(a));}
    friend Matrix operator * (const Matrix a,const Matrix b)
    {
        Matrix ret;
        for(int i=1;i<=3;i++)
            for(int j=1;j<=3;j++)
                for(int k=1;k<=3;k++) ret.a[i][j]+=a.a[i][k]*b.a[k][j];
        return ret;
    }
}f,g[36];
int Top;
struct Segment{
    long double k,b;
    Segment(){}
    Segment(long double K,long double B){k=K,b=B;}
}L[100005],Sta[100005];
const long double EPS=1e-12;
inline long double Fabs(long double x){return (x>=0?x:-x);}
inline long double Sign(long double x){return Fabs(x)<=EPS?0:(x>0?1:-1);}
long double K(Segment a,Segment b){return (a.b-b.b)/(b.k-a.k);}
bool cmp(Segment a,Segment b){return !Sign(a.k-b.k)?a.b<b.b:a.k<b.k;}
int main() {
    // #ifndef ONLINE_JUDGE
    freopen("demon.in","r",stdin);
    freopen("demon.out","w",stdout);
    // #endif
    long double Val=0;
    int n;ll T;read(n);read(T);
    for(int i=1;i<=n;i++)
    {
        long double a,b,p;
        scanf("%Lf %Lf %Lf",&a,&b,&p);
        L[i]=Segment(p,p*a); Val=max(Val,b*p); 
    }
    sort(L+1,L+1+n,cmp);int tot=0;
    for(int i=1;i<=n;i++)
        if(i==n||Sign(L[i].k-L[i+1].k)) L[++tot]=L[i];
    for(int i=1;i<=tot;i++)
    {
        while(Top>1&&Sign(K(Sta[Top],Sta[Top-1])-K(L[i],Sta[Top-1]))>=0) Top--;
        Sta[++Top]=L[i];
    }
    f.a[1][3]=1;ll Now=0;
    for(int i=1;i<=Top;i++)
    {
        long double pos=Now*Val-f.a[1][1];
        while(i<Top&&Sign(pos-K(Sta[i],Sta[i+1]))>=0) i++;
        if(i<Top) pos=K(Sta[i],Sta[i+1]);
        g[0].a[1][2]=g[0].a[1][3]=g[0].a[2][3]=0;
        g[0].a[2][2]=g[0].a[3][2]=g[0].a[3][3]=1;
        g[0].a[1][1]=1-Sta[i].k; g[0].a[2][1]=Sta[i].k*Val; g[0].a[3][1]=Sta[i].b;
        for(int j=1;j<=34;j++) g[j]=g[j-1]*g[j-1];
        for(int j=34;j>=0;j--)
            if((1ll<<j)+Now<T&&(i==Top||Sign(pos-(Now+(1ll<<j))*Val+(f*g[j]).a[1][1])>=0)) f=f*g[j],Now+=1ll<<j;
        f=f*g[0]; Now++;
        if(Now==T) break;
    }
    printf("%.10Lf",f.a[1][1]);
    return 0;
}
/*
清夜无尘。月色如银。酒斟时、须满十分。浮名浮利,虚苦劳神。叹隙中驹,石中火,梦中身。
虽抱文章,开口谁亲。且陶陶、乐尽天真。几时归去,作个闲人。对一张琴,一壶酒,一溪云。


*/

谁tm能想到在写这套题题解的时候NOIP就延期了,文化课文化课文化课欧耶!

posted @ 2022-11-24 09:41  Azazеl  阅读(130)  评论(0)    收藏  举报