CSP 模拟 3

joke3579场,

T1 abc猜想 ([ARC111A] Simple Math 2)

直接 \(\bmod \ c\),很难做,不难想到换一个和 \(c\) 有关的模数,\(c^2\) 很好,因为它除以 \(c\) 再模上 \(c\) 后为 \(0\)
\(x=a^b(\bmod\ c^2)\)\(a^b=k\cdot c^2+x\),除以 \(c\)\(c\) 后,前面那个东西没了,只剩 \(x\),所以答案就是 \(\left\lfloor\frac{x}{c}\right\rfloor\)

点击查看代码
#include<bits/stdc++.h>
#define int long long
typedef long long ll;
typedef unsigned long long ull;
inline int read(){char ch=getchar();int x=0,f=1;for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);return x*f;}
const int N=1e4+10;
const int MOD=1e9+7;
std::vector<int> sth;
inline int qpow(int a,int b,int mod){
	int res=1;
	for(;b;b>>=1,a=a*a%mod)if(b&1)res=res*a%mod;
	return res;
}
bool vis[N];
signed main(){
	// freopen("in.in","r",stdin);freopen("out.out","w",stdout);
	std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
	int a=read(),b=read(),c=read();
	int qc=c*c;
	int ans=qpow(a,b,qc);
	std::cout<<ans/c<<'\n';
}

T2 简单的排列最优化题 (CF819B Mister B and PR Shifts)

很容易的想到 \(n^2\) 做法,发现有很多东西都是重复记录了。考虑继承这些东西。
定义一个位置的价值为 \(a_i-i\),然后记一下这个价值出现的次数,发现每右移一位,这些价值的数量都会左移一位,即原来价值为 \(x\) 的现在价值为 \(x-1\)
同时,答案可以借助这些价值从上一次的答案转移过来,即价值为非正整数的会对答案做出 \(1\) 的贡献,价值为正整数的做出 \(-1\) 的贡献。
此时只需要支持单点修改区间查询即可,树状数组是个不错的选择(其实再记一下其他东西可以 \(\mathcal{O}(n)\),不过 ds 不要脑子)。
对于每次的右移,开个变量记录一下就行,对于末尾的点单独算。

点击查看代码
#include<bits/stdc++.h>
#define int long long
typedef long long ll;
typedef unsigned long long ull;
inline int read(){char ch=getchar();int x=0,f=1;for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);return x*f;}
const int N=4e6+10;
int t[N],len,n,a[N],last,ans=0,mink,minans;
inline void add(int pos,int x){int zc=pos+1;for(;pos<=len;pos+=pos&-pos)t[pos]+=x;}
inline int query(int l,int r){
	int res=0;
	l--;
	for(;l;l-=l&-l)res-=t[l];
    for(;r;r-=r&-r)res+=t[r];
	return res;
}
inline int real(int x){return x+n+last;}
signed main(){
	// freopen("in.in","r",stdin);freopen("out.out","w",stdout);
	std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
	n=read();len=n*4+1;
	for(int i=1;i<=n;++i){
		a[i]=read();
		add(real(a[i]-i),1);ans+=std::abs(a[i]-i);
	}
	mink=0,minans=ans;
	for(int i=1;i<=n-1;++i){
		int en=a[n-i+1];
		add(real(en-n),-1);
		ans+=-(std::abs(en-n))+en-1+query(1,real(0))-query(real(1),real(n-1));
		if(minans>ans){minans=ans,mink=i;}
		last++;
		add(real(en-1),1);
	}
	std::cout<<minans<<' '<<mink<<'\n';
}

T3 简单的线性做法题 (P4062 [Code+#1] Yazid 的新生舞会)

感觉是妙妙题,有较多做法,甚至有线性。这里介绍树状数组处理三阶前缀和的做法。
考虑对每个数单独处理。设 \(f_i\) 表示这个数到 \(i\) 位置时出现的次数,对于合法区间 \((l,r]\)\(f_r-f_l>\frac{r-l}{2}\Leftrightarrow 2f_r-r>2f_l-l\),直接拿树状数组维护 \(2f_i-i\) 的话,时间复杂度 \(\mathcal{O}(n^2\log n)\) 的。观察性质,发现对于每个数,它的 \(2f_i-i\) 是可以分段的。
image
简单观察发现,在两个相同数之间,它的 \(2f_i-i\) 是递减的,然后加一,然后又递减,而这些段的数量是 \(\mathcal{O}(n)\) 的,这启示我们去维护这些段。对于 \(a_i=2f_i-i\),要去前面找比他小的,\(T_i=\sum_{j=1}^i a_i\)\(W_i=\sum_{j=1}^i Ti\),树状数组上差分,设 \(d\)\(a\) 的差分数组,有 \(a_i=\sum_{j=1}^i\)

\[\begin{aligned} W_n&=\sum_{i=1}^n\sum_{j=1}^i\sum_{k=1}^j d_j\\ &=\sum_{i=1}^n\sum_{j=1}^i (i-j+1)d_j\\ &=\sum_{i=1}^n\frac{(n-i+2)(n-i+1)}{2}d_i\\ &=\frac{(n+1)(n+2)}{2}\sum_{i=1}^n d_i-n\sum_{i=1}^n i\cdot d_i+\frac{1}{2}\sum_{i=1}^n i(i-3)d_i \end{aligned} \]

开三个树状数组分别维护一下就好了,具体看代码。

点击查看代码
#include<bits/stdc++.h>
#define int long long
typedef long long ll;
typedef unsigned long long ull;
inline int read(){char ch=getchar();int x=0,f=1;for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);return x*f;}
const int N=1e6+10;
int n,a[N],d[N],c,temp[N],ans,t1[N],t2[N],t3[N];
std::vector<int> p[N];
inline void add(int l,int r,int x){
	if(l>r)return;
	l+=n+1,r+=n+1;r++;
	for(int i=l;i<=2*n+1;i+=i&-i)t1[i]+=x,t2[i]+=l*x,t3[i]+=(l*l-3*l)*x;
	for(int i=r;i<=2*n+1;i+=i&-i)t1[i]-=x,t2[i]-=r*x,t3[i]-=(r*r-3*r)*x;
}
inline int query(int l,int r){
	if(l>r)return 0;
	int res1=0,res2=0;
	l+=n+1,r+=n+1;l--;
	for(int i=r;i;i-=i&-i)res1+=(((r+1)*(r+2))>>1)*t1[i]-r*t2[i]+t3[i]/2;
	for(int i=l;i;i-=i&-i)res2+=(((l+1)*(l+2))>>1)*t1[i]-l*t2[i]+t3[i]/2;
	return res1-res2;
}
inline void work(int x){
	int num=0,last=0;
	for(int i:p[x]){
		num++;
		int zc=num*2-i;
		int l=zc-1,r=zc-1+i-1-last;
		ans+=query(l-1,r-1);
		add(l,r,1);
		last=i;
	}
	int zc=num*2-n;
	int l=zc,r=l+n-last;
	ans+=query(l-1,r-1);
	num=0;last=0;
	for(int i:p[x]){
		num++;
		int zc=num*2-i;
		int l=zc-1,r=zc-1+i-1-last;
		add(l,r,-1);
		last=i;
	}
}
signed main(){
	// freopen("in.in","r",stdin);freopen("out.out","w",stdout);
	std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
	n=read();
	for(int i=1;i<=n;++i){temp[i]=a[i]=read();}
	std::sort(temp+1,temp+1+n);
	int len=std::unique(temp+1,temp+1+n)-temp-1;
	for(int i=1;i<=n;++i)a[i]=std::lower_bound(temp+1,temp+1+len,a[i])-temp,p[a[i]].push_back(i);;
	for(int i=1;i<=len;++i)work(i);
	std::cout<<ans<<'\n';
}

T4 简单的线段树题

原题 P4145 上帝造题的七分钟 2 / 花神游历各国
这题太典了,开方到 \(1\) 后就没啥意义了,并且开不了多少次,直接魔改线段树维护即可。

点击查看代码
#include<bits/stdc++.h>
#define int long long
typedef long long ll;
typedef unsigned long long ull;
inline int read(){char ch=getchar();int x=0,f=1;for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);return x*f;}
const int N=1e6+10;
int a[N],n,m;
struct TREE{int num,sum;}t[N<<2];
inline void update(int p){
	t[p].num=t[p<<1].num+t[p<<1|1].num;
	t[p].sum=t[p<<1].sum+t[p<<1|1].sum;
}
inline void build(int p,int l,int r){
	if(l==r){t[p]={a[l]==1?1:0,a[l]};return;}
	int mid=l+r>>1;
	build(p<<1,l,mid);build(p<<1|1,mid+1,r);
	update(p);
}
inline void modi(int p,int l,int r,int x,int y){
	if(l>=x&&r<=y){
		if(t[p].num==r-l+1){return;}
		int mid=l+r>>1;
		if(l==r){t[p].sum=std::sqrt(t[p].sum);t[p].num=(t[p].sum==1?1:0);return;}
		if(t[p<<1].num<mid-l+1)modi(p<<1,l,mid,x,y);
		if(t[p<<1|1].num<r-mid)modi(p<<1|1,mid+1,r,x,y);
		update(p);
		return;
	}
	int mid=l+r>>1;
	if(x<=mid)modi(p<<1,l,mid,x,y);
	if(y>mid)modi(p<<1|1,mid+1,r,x,y);
	update(p);
}
inline int query(int p,int l,int r,int x,int y){
	if(l>=x&&r<=y)return t[p].sum;
	int mid=l+r>>1,res=0;
	if(x<=mid)res+=query(p<<1,l,mid,x,y);
	if(y>mid)res+=query(p<<1|1,mid+1,r,x,y);
	return res;
}
signed main(){
	// freopen("in.in","r",stdin);freopen("out.out","w",stdout);
	std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
	n=read();
	for(int i=1;i<=n;++i)a[i]=read();
	build(1,1,n);
	m=read();
	for(int i=1;i<=m;++i){
		int op=read(),l=read(),r=read();
		if(op){std::cout<<query(1,1,n,l,r)<<'\n';}
		else modi(1,1,n,l,r);
	}
}
posted @ 2024-07-25 14:01  Ishar-zdl  阅读(34)  评论(0)    收藏  举报