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\) ,那就是:
\(~~~~\) 于是我们 \(\mathcal{O(nT)}\) 做了。
\(~~~~\) 来换一下形式:
\(~~~~\) 哇塞,这是不是又是一个直线形式。把 \(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就延期了,文化课文化课文化课欧耶!