8.24考試總結(NOIP模擬47)[Prime·Sequence·Omeed]
時間帶著明顯的惡意,緩緩在我的頭頂流逝。
T1 Prime
解題思路
成功沒有簽上到。。。
一看資料範圍 \(R-L+1\le 10^7,R\le 10^{14}\) ,這肯定是判斷範圍內的數字是否可行之後直接暴力掃啊。。
然後我們就考慮線性篩,篩出 \(\min(\sqrt{R},K)\) 之間的素數。
算出素數來有什麼用呢(沒什麼用,所以我們篩完之後不用)
發現對於一個數字 \(x\) 而言,它除了本身之外的因數一定是 \(\le \sqrt{x}\) 的。
然後我們就可以根據已經篩出來的素數直接暴力向後更新倍數就好了,設素數個數是 \(n\) ,調和級數所以複雜度是 \(n\times ln\;n\)
code
#include<bits/stdc++.h> #define int long long #define ull unsigned long long #define f() cout<<"Pass"<<endl using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*f; } const int N=1e7+10; int l,r,m,ans,cnt,pri[N]; bool vis[N],b[N]; void Prime() { int temp=min((int)sqrt(r),m); for(int i=2;i<=temp;i++) { if(!vis[i]) pri[++cnt]=i; for(int j=1;j<=cnt&&pri[j]*i<=temp;j++) { vis[i*pri[j]]=true; if(i%pri[j]==0) break; } } } signed main() { l=read(); r=read(); m=read(); Prime(); int temp=min((int)sqrt(r),m); for(int i=1;i<=cnt;i++) for(int j=ceil((1.0*l)/(1.0*pri[i]));j<=r/pri[i];j++) b[j*pri[i]-l]=true; for(int i=l;i<=r;i++) { if(i<=temp){if(vis[i]) continue;} else if(b[i-l]) continue; ans^=i; } printf("%lld",ans); return 0; }
T2 Sequence
解題思路
矩陣乘法優化 DP。
先考慮怎麼算不同子序列個數。設 \(f_i\) 表示當前以i結尾的子序列個數,如果整個序列的
下一個元素是 \(x\), 那麼令 \(f_x=1+\sum\limits_{i=1}^k f_i\),其它的dp值保持不變。
發現無論下一個數字填什麼其實都是一樣的因此我們要最大化 \(\sum\limits_{i=1}^k f_i\),貪心選擇剩下 DP 值最小的,也就是最後出現位置最靠前的位置。
然後對於這樣的轉移我們可以選擇矩陣乘法,一次搞 k 次可以通過此題。
code
#include<bits/stdc++.h> #define int long long #define ull unsigned long long #define f() cout<<"Pass"<<endl using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*f; } const int N=1e6+10,M=110,mod=1e9+7; const double base=1e7; int n,m,K,f[M],s[N],id[N],e[M],fro[N]; struct Matrix { int s[M][M]; Matrix(){memset(s,0,sizeof(s));} Matrix friend operator * (Matrix x,Matrix y) { Matrix sum; for(int i=1;i<=K+1;i++) for(int j=1;j<=K+1;j++) { for(int k=1;k<=K+1;k++) sum.s[i][j]=(sum.s[i][j]+x.s[i][k]*y.s[k][j]%mod); sum.s[i][j]%=mod; } return sum; } }ans,mat; Matrix power(Matrix x,int y) { Matrix sum; for(int i=1;i<=K+1;i++) sum.s[i][i]=1; while(y) { if(y&1) sum=sum*x; x=x*x; y>>=1; } return sum; } bool comp(int x,int y) { return fro[x]<fro[y]; } signed main() { n=read(); m=read(); K=read(); int sum=0; for(int i=1,las;i<=n;i++) { s[i]=read(); las=sum; sum=(2*sum+1-f[s[i]]+mod)%mod; f[s[i]]=las+1; fro[s[i]]=i; } for(int i=1;i<=K;i++) id[i]=i; sort(id+1,id+K+1,comp); for(int i=1;i<=K;i++) ans.s[1][i]=f[id[i]]; ans.s[1][K+1]=1; for(int i=1;i<=K;i++) { memset(e,0,sizeof(e)); e[i]=1; sum=1; for(int j=1;j<=K;j++) { int temp=sum; sum=(sum*2-e[j]+mod)%mod; e[j]=temp; } for(int j=1;j<=K;j++) mat.s[i][j]=e[j]; } memset(e,0,sizeof(e)); sum=0; for(int i=1;i<=K;i++) { int temp=sum; sum=(sum*2+1-e[i]+mod)%mod; e[i]=temp+1; } for(int i=1;i<=K;i++) mat.s[K+1][i]=e[i]; mat.s[K+1][K+1]=1; sum=0; ans=ans*power(mat,m/K); for(int i=1;i<=K;i++) f[i]=ans.s[1][i],sum=(sum+f[i])%mod; for(int i=1;i<=m%K;i++) { int temp=sum; sum=(sum*2+1-f[i]+mod)%mod; f[i]=temp+1; } printf("%lld",sum); return 0; }
T3 Omeed
解題思路
大概有兩種打法吧。。
對於基礎分數的貢獻十分簡單在此不做過多的贅述。
對於連擊分數的每個位置 \(i\) 對於下一位的貢獻設為 \(f_i\),就有了下面的柿子:
\[f_i=p_i\times (f_{i-1}+1)+(1-p_i)\times f_{i-1}\times t \]經過層層化簡就可以得到類似於 \(f_{i}=k\times f_{L-1}+b\) 的柿子,然後發現 \(k\times f_{L-1}\) 啥用沒有,於是可以直接維護 \(b\) 的加和。
上面的是一種做法,但是實現起來比較麻煩,我直接。。。
下面講一下另一種做法。。。
還是化簡柿子,可以得到 \(\displaystyle f_k=\sum_{i=L}^{k}p_i\prod_{j=i+1}^{k}(t+p_i-p_i\times t)\)
然後對於每一個區間也是維護 \(k,b\) ,與上面做法不同的是這裡的取值與左端點 \(f_L\) 的取值有關係,因此需要對於每個區間的右端點進行維護,用於合併資訊。
有億點卡常。
code
#include<bits/stdc++.h>
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
#define ls x<<1
#define rs x<<1|1
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=5e5+10,mod=998244353;
int task,ta,tb,n,q,t,a,b,p[N],f[N];
int power(int x,int y)
{
int temp=1;
while(y)
{
if(y&1) temp=1ll*temp*x%mod;
x=1ll*x*x%mod;
y>>=1;
}
return temp;
}
int inv(int x){return power(x,mod-2)%mod;}
struct Segment_Tree
{
int dat,k,b,datk,datb;
}tre[N<<1];
void update(int x,int num)
{
tre[x].dat=tre[x].b=tre[x].datk=tre[x].datb=num;
tre[x].k=(t+num-1ll*num*t%mod+mod)%mod;
}
void push_up(int x)
{
tre[x].dat=(tre[ls].dat+tre[rs].dat)%mod;
tre[x].k=1ll*tre[ls].k*tre[rs].k%mod;
tre[x].b=(1ll*tre[rs].k*tre[ls].b+tre[rs].b)%mod;
tre[x].datk=(tre[ls].datk+1ll*tre[rs].datk*tre[ls].k)%mod;
tre[x].datb=(tre[ls].datb+1ll*tre[rs].datk*tre[ls].b+tre[rs].datb)%mod;
}
void insert(int x,int l,int r,int pos,int num)
{
if(l==r) return update(x,num),void();
int mid=(l+r)>>1;
if(pos<=mid) insert(ls,l,mid,pos,num);
else insert(rs,mid+1,r,pos,num);
push_up(x);
}
Segment_Tree query_seg(int x,int l,int r,int L,int R)
{
if(L<=l&&r<=R) return tre[x];
int mid=(l+r)>>1;
Segment_Tree ans1=(Segment_Tree){-1,0,0,0,0};
Segment_Tree ans2=(Segment_Tree){-1,0,0,0,0};
if(L<=mid) ans1=query_seg(ls,l,mid,L,R);
if(R>mid) ans2=query_seg(rs,mid+1,r,L,R);
if(~ans1.dat&&~ans2.dat)
return (Segment_Tree){(ans1.dat+ans2.dat)%mod,1ll*ans1.k*ans2.k%mod,(1ll*ans2.k*ans1.b+ans2.b)%mod,(ans1.datk+1ll*ans2.datk*ans1.k)%mod,(ans1.datb+1ll*ans2.datk*ans1.b+ans2.datb)%mod};
if(~ans1.dat) return ans1;
return ans2;
}
signed main()
{
task=read();
n=read(); q=read();
ta=read(); tb=read();
t=1ll*ta*inv(tb)%mod;
a=read(); b=read();
for(int i=1,pa,pb;i<=n;i++)
{
pa=read(); pb=read();
p[i]=1ll*pa*inv(pb)%mod;
insert(1,1,n,i,p[i]);
}
while(q--)
{
int opt,pos,pa,pb,l,r;
opt=read();
if(!opt)
{
pos=read(); pa=read(); pb=read();
p[pos]=1ll*pa*inv(pb)%mod;
insert(1,1,n,pos,p[pos]);
continue;
}
l=read(); r=read();
if(l==r){printf("%lld\n",1ll*(a+b)*p[l]%mod);continue;}
Segment_Tree temp=query_seg(1,1,n,l+1,r);
printf("%lld\n",(1ll*a*(1ll*temp.dat+p[l])%mod+1ll*(1ll*p[l]*temp.datk%mod+1ll*temp.datb+p[l])*b%mod)%mod);
}
return 0;
}