Comet OJ

Preface

最近板刷比赛都跟着bzt神仙的节奏来啊,看到他在打Comet OJ 所以我也来打了

看比赛榜就知道这一次前5题都很多人过但F无人AC,bzt说正解是LCT优化长链剖分,我立刻就不想写了QAQ


A 杀手皇后

SB题不解释

#include<iostream>
#include<string>
using namespace std;
int n; string s,ans;
int main()
{
	ios::sync_with_stdio(0); cin>>n;
	for (register int i=1;i<=n;++i) cin>>s,(ans==""||s<ans)&&(ans=s,0);
	return cout<<ans,0;
}

B 支援城市

把平方拆了发现我们只需要维护(sum_{i=1}^n w_i^2)(sum_{i=1}^n w_i)就可以对于每一个(xO(1))计算了

#include<cstdio>
using namespace std;
const int N=100005;
int n,a[N]; long long sum,psum;
int main()
{
	register int i; for (scanf("%d",&n),i=1;i<=n;++i)
	scanf("%d",&a[i]),sum+=a[i],psum+=1LL*a[i]*a[i];
	for (i=1;i<=n;++i) printf("%lld ",psum+1LL*n*a[i]*a[i]-2LL*sum*a[i]);
	return 0;
}

C 符文能量

首先发现符文石无论怎样合并最后的答案都是一样的,因此我们先把从左到右的答案全算出来

然后考虑可以精炼一个区间,那么我们发现这个区间的两端都乘上了一个(k-1)的系数,然后中间的数乘上了(k^2-1)的系数

那我们直接DP设(f_i)为包含(i)的答案,每次按(i)为左右中三个位置更新答案即可

#include<cstdio>
#include<iostream>
#define RI register int
using namespace std;
const int N=100005;
int n,k,a[N],b[N]; long long f[N],ret,dlt;
int main()
{
	RI i; for (scanf("%d%d",&n,&k),i=1;i<=n;++i)
	{
		scanf("%d%d",&a[i],&b[i]); ret+=b[i-1]*a[i];
		dlt=min(dlt,f[i-1]+1LL*(k-1)*b[i-1]*a[i]);
		f[i]=min(f[i],min(1LL*(k-1)*b[i-1]*a[i],f[i-1]+1LL*(k*k-1)*b[i-1]*a[i]));
	}
	return printf("%lld",ret+dlt),0;
}

D 菜菜种菜

bzt神仙刚开始用sort然后T掉了,所以我就学乖了没有sort……

首先我们考虑对于每一块菜地预处理出编号比它小的最大值(l_i)以及编号比它大的最小值(r_i),那么一个区间([L,R])合法当且仅当(l_i<L)(R<r_i)

所以现在我们相当于要计算(sum_{i=L}^R [l_i<Lle R<r_i]s_i),我们稍加转化就变成(sum_{i=1}^n[l_i+1le Lle ile Rle r_i-1]s_i)

考虑我们按序枚举(L),每次再将(L-1)位置上的所有(l_i=L-1)(i)([i,r_i-1])区间加上(s_i),然后将(i=L-1)([i,r_i-1])(s_i)扣去,再查询(R)上的值即为答案

树状数组+vector即可维护上述过程,复杂度(O(nlog n))(树状数组的(log)快到难以想象)

#include<cstdio>
#include<cctype>
#include<utility>
#include<vector>
#include<iostream>
#define RI register int
#define CI const int&
#define Tp template <typename T>
#define pb push_back
#define mp make_pair
#define fi first
#define se second
using namespace std;
const int N=1e6+5;
typedef pair <int,int> pi;
typedef vector <pi>::iterator VP;
int n,m,q,l[N],r[N],x,y,a[N],ans[N],mx; long long ret; vector <pi> et[N],ques[N];
class FileInputOutput
{
	private:
		static const int S=1<<21;
		#define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,S,stdin),A==B)?EOF:*A++)
		#define pc(ch) (Ftop!=Fend?*Ftop++=ch:(fwrite(Fout,1,S,stdout),*(Ftop=Fout)++=ch))
		char Fin[S],Fout[S],*A,*B,*Ftop,*Fend; int pt[15];
	public:
		Tp inline void read(T& x)
		{
			x=0; char ch; while (!isdigit(ch=tc()));
			while (x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));
		}
		Tp inline void write(T x)
		{
			RI ptop=0; while (pt[++ptop]=x%10,x/=10);
			while (ptop) pc(pt[ptop--]+48); pc('
');
		}
		#undef tc
		#undef pc
}F;
class Tree_Array
{
	private:
		int bit[N];
		#define lowbit(x) x&-x
		inline void add(RI x,CI mv)
		{
			for (;x<=n;x+=lowbit(x)) bit[x]+=mv;
		}
	public:
		inline void modify(CI l,CI r,CI mv)
		{
			if (l>r) return; add(l,mv); add(r+1,-mv);
		}
		inline int query(RI x,int ret=0)
		{
			for (;x;x-=lowbit(x)) ret+=bit[x]; return ret;
		}
		#undef lowbit
}BIT;
int main()
{
	//freopen("D.in","r",stdin); freopen("D.out","w",stdout);
	RI i; for (F.read(n),F.read(m),F.read(q),i=1;i<=n;++i) F.read(a[i]),r[i]=n+1;
	for (i=1;i<=m;++i) F.read(x),F.read(y),y<x&&(l[x]=max(l[x],y)),y>x&&(r[x]=min(r[x],y));
	for (i=1;i<=q;++i) F.read(x),F.read(y),ques[x].pb(mp(y,i)),mx=max(mx,y);
	for (i=1;i<=n;++i) et[l[i]].pb(mp(r[i],i)); for (i=1;i<=mx;++i)
	{
		for (VP it=et[i-1].begin();it!=et[i-1].end();++it)
		BIT.modify(it->se,it->fi-1,a[it->se]); BIT.modify(i-1,r[i-1]-1,-a[i-1]);
		for (VP it=ques[i].begin();it!=ques[i].end();++it) ans[it->se]=BIT.query(it->fi);
	}
	for (i=1;i<=q;++i) ret^=1LL*i*ans[i]; return printf("%lld",ret),0;
}

E 神奇函数

神仙数论题,首先我们冷静分析一下(f(x))的本质就是求(x)在分解质因数之后将每个质因子的次数除以(2)(下取整)

这个很好理解,我们观察第二个式子,发现就是一个不停提出质因数的过程

所以我们发现(f(x))的值域就是根号级别的,我们可以枚举(f(x))的值,然后算出有多少个数的值为这个

实际上,我们可以得出关于答案的这样一个式子:

[ans=sum_{i=1}^{sqrt n} icdot g(frac{n}{i^2}) ]

其中(g(x))表示([1,x])内不含有平方因子的数的个数

为什么是这个函数呢,很简单,因为一旦它有了平方因子那个因子就可以被提出来,所以值就不是当前的值了

考虑(g(x)=sum_{i=1}^x mu(i)^2),运用一下经典容斥就会发现它其实是(sum_{i=1}^{sqrt{x}}mu(i)cdot lfloorfrac{n}{i^2} floor)

这样这题大致就可以做了,我们对于枚举答案的一层和计算(g(x))的时候都除法分块一下,复杂度就是(O(n^{frac{2}{3}}))

如果直接这样写就会T掉好多点,因此我们还要预处理一下(le 10^7)(g(x)),直接回答即可

#include<cstdio>
#include<cmath>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
typedef long long LL;
const int N=1e7;
int t,prime[N+5],cnt,mu[N+5],g[N+5]; bool vis[N+5]; LL n,ans;
#define Pi prime[j]
inline void init(CI n)
{
	RI i,j; for (mu[1]=vis[1]=1,i=2;i<=n;++i)
	{
		if (!vis[i]) prime[++cnt]=i,mu[i]=-1;
		for (j=1;j<=cnt&&i*Pi<=n;++j)
		{
			vis[i*Pi]=1; if (i%Pi) mu[i*Pi]=-mu[i]; else break;
		}
	}
	for (i=1;i<=n;++i) g[i]=g[i-1]+mu[i]*mu[i];
	for (i=1;i<=n;++i) mu[i]+=mu[i-1];
}
#undef Pi
inline LL G(const LL& x)
{
	if (x<=N) return g[x];
	int lim=sqrt(x); LL ret=0; for (RI l=1,r;l<=lim;l=r+1)
	r=min(lim,(int)sqrt(x/(x/(1LL*l*l)))),ret+=1LL*(mu[r]-mu[l-1])*(x/(1LL*l*l));
	return ret;
}
inline LL sum(CI l,CI r)
{
	return 1LL*(l+r)*(r-l+1)>>1LL;
}
int main()
{
	//freopen("E.in","r",stdin); freopen("E.out","w",stdout);
	for (init(N),scanf("%d",&t);t;--t)
	{
		scanf("%lld",&n); int lim=sqrt(n); ans=0; for (RI l=1,r;l<=lim;l=r+1)
		r=min(lim,(int)sqrt(n/(n/(1LL*l*l)))),ans+=1LL*sum(l,r)*G(n/(1LL*l*l));
		printf("%lld
",ans);
	}
	return 0;
}

F 黄金体验

LCT优化长链剖分,谁爱写谁写

咕咕咕


Postscript

比赛体验还好,算是混了个平均水平吧(感谢B指导和陈指导的指导)

原文地址:https://www.cnblogs.com/cjjsb/p/11349640.html