【比赛】暑假集训CSP提高模拟3

T1 abc猜想 100Pts
原题 Simple Math 2。
签到题。
对于任意整数 \(c\) 有
\[\lfloor \frac{a}{b} \rfloor + c = \lfloor
\frac{a}{b} + c \rfloor = \lfloor \frac{a+bc}{b} \rfloor
\]
设 $ \lfloor \frac{a^b}{c} \rfloor=kc+r(0 \le r < c)$,其中 \(r\) 为答案,那么 \(r=\lfloor \frac{a^b}{c} \rfloor-kc=\lfloor \frac{a^b-kc^2}{c} \rfloor=\lfloor \frac{a^b \mod c^2}{c} \rfloor\)。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a,b,c;
ll qp(__int128 a,ll b,ll mod){
__int128 ans=1;
while(b){
if(b&1)ans=ans*a%mod;
b>>=1;
a=a*a%mod;
}
return ans;
}
int main(){
cin>>a>>b>>c;
ll d=qp(a,b,c*c);
ll e=d%c;
cout<<(d-e)/c;
return 0;
}
T2 简单的排列最优化题 45 Pts
最终的答案是 \(\sum_{i=1}^n \limits |p_i-i|\),挂绝对值,所以将 \(p_i\) 分为两类:\(p_i-i>0\) 和 \(p_i-i \le 0\)
然后我们只需要从前往后扫一遍,每次更新扔到队首元素的贡献并处理临界即可。
点击查看代码
#include<bits/stdc++.h>
#define int ll
using namespace std;
typedef long long ll;
const int N=1e6+5;
ll a[N],cnt[N*2];
ll n,bc,bt,sc,st;
main(){
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++){
if(a[i]>i){
bc++;
bt+=a[i]-i;
cnt[a[i]-i]++;
}
else{
sc++;
st+=i-a[i];
}
}
ll ans=bt+st,k=0;
for(int i=1;i<n;i++){
bt-=bc;
bc-=cnt[i];
st+=sc;
sc+=cnt[i];
ll x=a[n-i+1];
st-=n-x+1;
sc--;
if(x>1){
cnt[x+i-1]++;
bt+=x-1;
bc++;
}
else sc++;
if(bt+st<ans){
ans=bt+st;
k=i;
}
}
cout<<k<<" "<<ans;
return 0;
}
T3 简单的线性做法题 25Pts
原题 Yazid 的新生舞会。
其实现在还不是很明白,也没有代码,挂一下官方题解吧。

T4 简单的线段树题 30Pts
显然,开方最多开 \(7\) 次,所以对于每一次询问我们直接向下推到一个还能开方的区间并开方即可。
用线段树实现的,然后再校内 oj 上被卡常了,所以又去贺了一版并查集 + 树状数组的。
线段树
#include<bits/stdc++.h>
#define int ll
using namespace std;
typedef long long ll;
const int N=1e5+5;
int n,m,a[N];
struct tree{
int l,r,maxn,sum;
}t[N<<2];
void pushup(int k){
t[k].sum=t[k<<1].sum+t[k<<1|1].sum;
t[k].maxn=max(t[k<<1].maxn,t[k<<1|1].maxn);
}
void build(int k,int l,int r){
t[k]={l,r,a[l],a[l]};
if(l==r)return;
int mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
pushup(k);
}
void update(int k,int l,int r){
if(t[k].l==t[k].r&&l<=t[k].l&&t[k].r<=r){
t[k].sum=t[k].maxn=sqrt(t[k].sum);
return;
}
int mid=(t[k].l+t[k].r)>>1;
if(l<=mid&&t[k<<1].maxn>1)update(k<<1,l,r);
if(mid<r&&t[k<<1|1].maxn>1)update(k<<1|1,l,r);
pushup(k);
}
int query(int k,int l,int r){
if(l<=t[k].l&&t[k].r<=r)return t[k].sum;
int mid=(t[k].l+t[k].r)>>1;
int ans=0;
if(l<=mid)ans+=query(k<<1,l,r);
if(r>mid)ans+=query(k<<1|1,l,r);
return ans;
}
main(){
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
build(1,1,n);
cin>>m;
for(int i=1,op,l,r;i<=m;i++){
cin>>op>>l>>r;
if(l>r)swap(l,r);
if(op==0)update(1,l,r);
else cout<<query(1,l,r)<<"\n";
}
return 0;
}
树状数组
#include<bits/stdc++.h>
#define int ll
#define lowbit(x) ((x)&(-(x)))
using namespace std;
typedef long long ll;
const int N=1e6+5;
ll n,m,fa[N],a[N],c[N];
int find(int x){
if(x!=fa[x])fa[x]=find(fa[x]);
return fa[x];
}
void add(int x,ll val){
while(x<=n){
c[x]+=val;
x+=lowbit(x);
}
}
ll query(int x){
ll ans=0;
while(x){
ans+=c[x];
x-=lowbit(x);
}
return ans;
}
main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
add(i,a[i]);
fa[i]=i;
}
cin>>m;
fa[n+1]=n+1;
for(int i=1,op,l,r;i<=m;i++){
cin>>op>>l>>r;
if(op==0){
for(int i=l;i<=r;){
int f=find(i);
if(f!=i)i=f;
else{
ll t=sqrt(a[i]);
add(i,t-a[i]);
a[i]=t;
if(a[i]==1){
fa[i]=i+1;
i=find(i);
}
else i++;
}
}
}
else{
cout<<query(r)-query(l-1)<<"\n";
}
}
return 0;
}