首页 > 其他分享 >做题记录

做题记录

时间:2022-11-09 16:01:23浏览次数:64  
标签:frac limits 记录 int sum times define

P3157 [CQOI2011]动态逆序对

链接

考虑一个逆序对在什么时候会对答案造成影响。

假设我们想要找到第 \(i\) 个结点被删除时所减少的逆序对个数。

记 \(a_i\) 表示第 \(i\) 个点的权值,\(t_i\) 表示第 \(i\) 个点的删除时间。那么满足条件的逆序对满足如下两个条件之一:

  1. \(t_i > t_j , i>j , a_i<a_j\)。
  2. \(t_i > t_j , i<j , a_i>a_j\)。

这是一个显然的三维偏序,跑两次 \(\texttt{cdq}\) 分治即可。

时间复杂度 \(O(n\log^2 n)\)。

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define vi vector<int>
#define pii pair<int,int>
#define pb(x) push_back(x)
#define lowbit(x) x&-x
using namespace std;
const int N=2e5+10;
struct node{
	int pos,t,a,ans;
}a[N];
ll ans;
int n,m,T,b[N],d[N],k,pos[N];
inline int read(){
	int s=0,f=0;
	char ch=getchar();
	while(ch>'9'||ch<'0') f|=(ch=='-'),ch=getchar();
	while(ch<='9'&&ch>='0') s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
	return f?-s:s;
}
inline void add(int x,int c){for(;x<=n;x+=lowbit(x)) d[x]+=c;}
inline int query(int x,int res=0){for(;x;x-=lowbit(x)) res+=d[x];return res;}
inline bool cmp1(node a,node b){
	return a.t>b.t;
}
inline bool cmp2(node a,node b){
	return a.pos<b.pos;
}
inline bool cmp3(node a,node b){
	return a.pos>b.pos;
}
inline bool cmp4(node a,node b){
	return a.t<b.t;
}
inline void solve(int l,int r){
	if(l==r) return ;
	int mid=(l+r)>>1;
	solve(l,mid),solve(mid+1,r);
	sort(a+l,a+mid+1,cmp2),sort(a+mid+1,a+r+1,cmp2);
	int maxn=l;
	for(register int i=mid+1;i<=r;++i){
		while(a[maxn].pos<a[i].pos&&maxn<=mid){
			add(a[maxn].a,1);
			++maxn;
		}
		a[i].ans+=query(n)-query(a[i].a);
	}
	for(register int i=l;i<maxn;++i) add(a[i].a,-1);
}
inline void solve2(int l,int r){
	if(l==r) return ;
	int mid=(l+r)>>1;
	solve2(l,mid),solve2(mid+1,r);
	sort(a+l,a+mid+1,cmp3),sort(a+mid+1,a+r+1,cmp3);
	int maxn=l;
	for(register int i=mid+1;i<=r;++i){
		while(a[maxn].pos>a[i].pos&&maxn<=mid){
			add(a[maxn].a,1);
			++maxn;
		}
		a[i].ans+=query(a[i].a);
	}
	for(register int i=l;i<maxn;++i) add(a[i].a,-1);
}
int main(){
	n=read(),m=read();
	for(register int i=1;i<=n;++i){
		int x=read();
		a[i].pos=i;
		a[i].a=x;
		pos[x]=i;
		b[i]=x;
	}
	for(register int i=1;i<=m;++i){
		int x=read();
		a[pos[x]].t=i;
	}
	for(register int i=1;i<=n;++i) if(a[i].t==0) a[i].t=m+1;
	for(register int i=n;i>=1;--i) ans+=query(b[i]),add(b[i],1);
	for(register int i=1;i<=n;++i) d[i]=0;
	sort(a+1,a+n+1,cmp1);
	solve(1,n);
	sort(a+1,a+n+1,cmp1);
	for(register int i=1;i<=n;++i) d[i]=0;
	solve2(1,n);
	sort(a+1,a+n+1,cmp4);
	for(register int i=1;i<=m;++i) printf("%lld\n",ans),ans-=a[i].ans;
	return 0;
}

P3935 Calculating

链接

发现 \(f_i\) 即为 \(i\) 的因子个数。

式子可以变为

\[\sum\limits_{i=l}^r \sum_{d | i} 1 \]

套路的,将 \(d\) 拉到前面去。

\[\sum\limits_{d=1}^n \sum\limits_{i=1}^{\large \lfloor \frac{n}{d}\rfloor} 1 \]

明显可以把后面那个 \(\sum\) 搞掉。

那么就是

\[\sum\limits_{d=1}^n \lfloor \frac{n}{d} \rfloor \]

整除分块即可。

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define int long long
#define vi vector<int>
#define pii pair<int,int>
#define pb(x) push_back(x)
#define lowbit(x) x&-x
using namespace std;
const int mod=998244353;
ll ans;
int n,m,T;
inline int read(){
	int s=0,f=0;
	char ch=getchar();
	while(ch>'9'||ch<'0') f|=(ch=='-'),ch=getchar();
	while(ch<='9'&&ch>='0') s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
	return f?-s:s;
}
inline int calc(int n){
	int res=0;
	for(register int l=1,r;l<=n;l=r+1){
		r=n/(n/l);
		res=(res+(r-l+1)%mod*(n/l)%mod)%mod;
	}
	return res;
}
signed main(){
	n=read(),m=read();
	printf("%lld\n",(calc(m)-calc(n-1)+mod)%mod);
	return 0;
}

P1829 [国家集训队]Crash的数字表格 / JZPTAB

链接

莫反练手题。

先推一推式子。

\[\begin{equation} \begin{aligned} &\sum\limits_{i=1}^n \sum\limits_{j=1}^m lcm(i,j)\\ =&\sum\limits_{i=1}^n \sum\limits_{j=1}^m \frac{i\times j}{\gcd(i,j)}\\ =&\sum\limits_{d=1}^n d \sum\limits_{i=1}^{\large \lfloor \frac{n}{d} \rfloor} \sum\limits_{j=1}^{\large \lfloor \frac{m}{d} \rfloor} i \times j \times [\gcd(i,j) = 1] \end{aligned} \end{equation} \]

考虑单独计算后面的部分。

我们记 \(f(n,m) = \sum\limits_{i=1}^n \sum\limits_{j=1}^m i \times j \times [\gcd(i,j)=1]\)。

考虑使用莫反将它化简。

\[\begin{equation} \begin{aligned} &\sum\limits_{i=1}^n \sum\limits_{j=1}^m \sum\limits_{d|\gcd(i,j)} \mu(d) \times i \times j\\ =&\sum\limits_{d=1}^n \mu(d) \sum\limits_{i=1}^{\large \lfloor \frac{n}{d} \rfloor} \sum\limits_{j=1}^{\large \lfloor \frac{m}{d} \rfloor} i \times d \times j \times d\\ =&\sum\limits_{d=1}^n \mu(d) \times d^2 \sum\limits_{i=1}^{\large \lfloor \frac{n}{d} \rfloor} \sum\limits_{j=1}^{\large \lfloor \frac{m}{d} \rfloor} i \times j \end{aligned} \end{equation} \]

这样就看起来非常可做了对吧。

前面的一部分可以线性筛 \(+\) 前缀和预处理,后面一部分 \(O(1)\) 算法显然,化简一下即可,这里不赘述。

带回原式,式子变成了 \(\sum\limits_{d=1}^n d \times f(\lfloor \frac{n}{d} \rfloor,\lfloor \frac{m}{d} \rfloor)\)。

对于后面的部分整除分块即可。

总复杂度就是 \(O(\sqrt{n} \times \sqrt{n}) = O(n)\)。

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define int long long
#define vi vector<int>
#define pii pair<int,int>
#define pb(x) push_back(x)
#define lowbit(x) x&-x
using namespace std;
const int N=1e7+10;
const int mod=20101009;
const int inv=10050505;
ll ans;
bool vist[N];
int n,m,T,prime[N],tot,mo[N],s[N];
inline int read(){
	int s=0,f=0;
	char ch=getchar();
	while(ch>'9'||ch<'0') f|=(ch=='-'),ch=getchar();
	while(ch<='9'&&ch>='0') s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
	return f?-s:s;
}
inline void init(){
	mo[1]=1;
	for(register int i=2;i<=n;++i){
		if(!vist[i]) prime[++tot]=i,mo[i]=-1;
		for(register int j=1;j<=tot&&1ll*prime[j]*i<=n;++j){
			vist[i*prime[j]]=1;
			if(i%prime[j]==0){
				mo[i*prime[j]]=0;
				break;
			}
			mo[i*prime[j]]=-mo[i];
		}
	}
}
inline int g(int n,int m){
	return n*(n+1)%mod*inv%mod*m%mod*(m+1)%mod*inv%mod;
}
inline int qpow(int x,int y){
	int res=1;
	while(y){
		if(y&1) res=1ll*res*x%mod;
		x=1ll*x*x%mod;
		y>>=1;
	}
	return res;
}
inline int calc(int n,int m){
	int res=0;
	for(register int l=1,r;l<=n;l=r+1){
		r=min(n/(n/l),m/(m/l));
		res=(res+g(n/l,m/l)*((s[r]-s[l-1]+mod)%mod)%mod)%mod;
	}
	return res;
}
inline int work(int n,int m){
	int res=0;
	for(register int l=1,r;l<=n;l=r+1){
		r=min(n/(n/l),m/(m/l));
		res=(res+calc(n/l,m/l)*((r+l)%mod*(r-l+1)%mod*inv%mod)%mod)%mod;
	}
	return res;
}
signed main(){
	n=read(),m=read();
	if(n>m) swap(n,m);
	init();
	for(register int i=1;i<=n;++i) s[i]=(1ll*s[i-1]+1ll*i*i%mod*mo[i]%mod)%mod;
	printf("%lld\n",work(n,m));
	return 0;
}

标签:frac,limits,记录,int,sum,times,define
From: https://www.cnblogs.com/kqwlp/p/16874091.html

相关文章