NOIP提高组模拟赛21

夜莺与玫瑰

想了大概\(2h\)也没搞出来,真的是。。。我太菜了

最后连\(60pts\)暴力都没调出来(式子没有\(max\)),还是时间分配不合理,留给打爆力的时间太少了。。。。。。。(最后\(10min\)能打出来才是奇迹好吧)

这题一看题面就想到仪仗队,然后就想用欧拉函数,就歪了

欧拉函数用不上,但是思想还是有借用的

用向量\((a,b)\)表示一个斜率的直线,借鉴思想容易发现当\(gcd(a,b)==1\)时才是需要统计的斜率

考虑有多少斜率为\((a,b)\)的直线(有多少\(a*b\)的矩形),显然\((n-a)*(m-b)\)个,但是这样会重复计算,我们需要的是最左下方的一个,这样的矩形满足\(x<a||y<b\),(\((x,y)\)是矩形左下方点),或很恶心,正难则反,有\((n-a-a)*(m-b-b)\)个重复计算的,所以对于斜率\((a,b)\),有\((n-a)*(m-b)-(n-a-a)*(m-b-b)\)个矩形

吗?

\(n-a-a\)\(m-b-b\)可能小于0,这个时候取0就可以了
(我是\(**\))

(下面的\(\sum\)\(n,m\)好像需要\(-1\),懒得改了)
所以答案为

\(\displaystyle\sum_{a=1}^{n}\sum_{b=1}^{m}[gcd(a,b)==1]((n-a)*(m-b)-max(n-a-a,0)*max(m-b-b,0))\)

显然会\(TLE\),考虑化简这个式子,或者使用莫比乌斯反演(我太菜了不会,请找\(artalter\)大佬学习)

\(\displaystyle\sum_{i=a}^{n}\sum_{j=b}^{m}[gcd(a,b)==1](n-a)*(m-b)-\displaystyle\sum_{a=1}^{n}\sum_{b=1}^{m}[gcd(a,b)==1]max(n-a-a,0)*max(m-b-b,0)\)

\(max\)留着干啥?扔掉

\(\displaystyle\sum_{a=1}^{n}(n-a)\sum_{b=1}^{m}[gcd(a,b)==1](m-b)-\displaystyle\sum_{a=1}^{\lfloor \frac{n}{2}\rfloor}\sum_{b=1}^{\lfloor \frac{m}{2}\rfloor}[gcd(a,b)==1](n-a*2)(m-b*2)\)

\(\displaystyle\sum_{a=1}^{n}(n-a)\sum_{b=1}^{m}[gcd(a,b)==1](m-b)-\displaystyle\sum_{a=1}^{\lfloor \frac{n}{2}\rfloor}(n-a*2)\sum_{b=1}^{\lfloor \frac{m}{2}\rfloor}[gcd(a,b)==1](m-b*2)\)

\(\displaystyle\sum_{a=1}^{n}(n-a)(m\sum_{b=1}^{m}[gcd(a,b)==1]-\sum_{b=1}^{m}[gcd(a,b)==1]b)-\displaystyle\sum_{a=1}^{\lfloor \frac{n}{2}\rfloor}(n-a*2)(m\sum_{b=1}^{\lfloor \frac{m}{2}\rfloor}[gcd(a,b)==1]-\sum_{b=1}^{\lfloor \frac{m}{2}\rfloor}[gcd(a,b)==1](b*2))\)

\(O(n^2logn)\)预处理出来\(f[i][j]=\displaystyle\sum_{l=1}^{j}[gcd(i,l)==1]\)\(g[i][j]=\displaystyle\sum_{l=1}^{j}[gcd(i,l)==1]l\)前缀和优化

原式转化\(\displaystyle\sum_{a=1}^{n}(n-a)(mf[a][m]-g[a][m])-\displaystyle\sum_{a=1}^{\lfloor \frac{n}{2}\rfloor}(n-a*2)(mf[a][\lfloor \frac{m}{2}\rfloor]-2f[a][\lfloor \frac{m}{2}\rfloor])\)

复杂度\(O(n^2logn+Tn)\)

code
#include <cstring>
#include <cstdio>

using namespace std;
typedef long long ll;
const int maxn=4005;
const int mod=1073741824;

int n,m;
int rem[maxn][maxn];
int ren[maxn][maxn];

int gcd(int x,int y){
	if(y==0)return x;
	return gcd(y,x%y);
}
void pre(){
	for(int i=1;i<=4000;++i)
	  for(int j=1;j<=4000;++j){
		  rem[i][j]=rem[i][j-1],ren[i][j]=ren[i][j-1];
	      if(gcd(i,j)==1)++rem[i][j],ren[i][j]+=j;
		}
}

void work(){
	ll ans=0;
	int ls=m-1;
	for(int a=1;a<n;++a)
	  ans=(ans+(n-a)*(rem[a][ls]*m-ren[a][ls])%mod)%mod;
	ls=n/2;int lr=m/2;
	for(int a=1;a<=ls;++a)
	  ans=(ans-((n-a-a)*(rem[a][lr]*m-2*ren[a][lr])%mod)+mod)%mod;
	printf("%lld\n",(ans*2+n+m)%mod);
}

int main(){
	pre();
	int T;scanf("%d",&T);
	for(int ask=1;ask<=T;++ask){
		scanf("%d%d",&n,&m);
		work();
	}
	return 0;
}

B. 影子

距离正解那么近又那么远。

什么\(min\)太恶心了,对点权排序从大到小加点找直径避免掉

使用并查集维护某个子树中的直径及两个端点,每次加入新点将它与周围集合合并

设合并\(x,y\)两棵子树

新子树直径为\(x\)的直径、\(y\)的直径、\(x\)直径端点到\(y\)直径端点\(6\)种可能

用树剖或者倍增维护两点距离即可

code
#include <cstring>
#include <cstdio>
#include<algorithm>

using namespace std;
const int maxn=100005;
typedef long long ll;

ll len[maxn][21];
struct node{int val,id;}d[maxn];
struct edge{int net,to,val;}e[maxn<<1|1];
struct SET{ll maxd;int l,r,fa;}f[maxn];
bool cmp(node x,node y){return x.val>y.val;}
int head[maxn],tot,n,fa[maxn][21],dep[maxn];
bool flag[maxn];
void add(int u,int v,int w){
   e[++tot].net=head[u];head[u]=tot;
   e[tot].to=v;e[tot].val=w;
}

void dfs(int x){
	for(int i=head[x];i;i=e[i].net){
		int v=e[i].to;
		if(v==fa[x][0])continue;
		fa[v][0]=x;dep[v]=dep[x]+1;
		len[v][0]=e[i].val;
		dfs(v);
	}
}
int gf(int x){
	return f[x].fa=f[x].fa==x?x:gf(f[x].fa);
}
ll dis(int u,int v){
	if(dep[u]<dep[v])swap(u,v);
	ll ans=0;
	for(int i=20;i>=0;--i)if(dep[u]-dep[v]>=(1<<i))ans+=len[u][i],u=fa[u][i];
	if(u==v)return ans;
	for(int i=20;i>=0;--i)if(fa[u][i]!=fa[v][i])ans+=len[u][i]+len[v][i],u=fa[u][i],v=fa[v][i];
	return ans+len[u][0]+len[v][0];
}

ll hb(int x,int y){
	x=gf(x);y=gf(y);
	if(x==y)return f[x].maxd;
	int rl=0,rr=0;ll mx=-1,di;
	di=dis(f[x].l,f[y].l);if(di>mx)mx=di,rl=f[x].l,rr=f[y].l;
	di=dis(f[x].l,f[y].r);if(di>mx)mx=di,rl=f[x].l,rr=f[y].r;
	di=dis(f[x].r,f[y].l);if(di>mx)mx=di,rl=f[x].r,rr=f[y].l;
	di=dis(f[x].r,f[y].r);if(di>mx)mx=di,rl=f[x].r,rr=f[y].r;
	f[y].fa=x;
	if(f[x].maxd<f[y].maxd)f[x].maxd=f[y].maxd,f[x].l=f[y].l,f[x].r=f[y].r;
	if(f[x].maxd<mx)f[x].maxd=mx,f[x].l=rl,f[x].r=rr;
	return f[x].maxd;
}

ll work(int x){
	ll ans=0;
	for(int i=head[x];i;i=e[i].net){
		int v=e[i].to;
		if(!flag[v])continue;
		ans=max(ans,hb(x,v));
	}
	return ans;
}

int main(){
	// freopen("b.in","r",stdin);
	int T;scanf("%d",&T);
	for(int ask=1;ask<=T;++ask){
		scanf("%d",&n);
		for(int i=1;i<=n;++i)scanf("%d",&d[i].val);
		
		tot=0;
		memset(fa,0,sizeof(fa));
		memset(len,0,sizeof(len));
		for(int i=1;i<=n;++i)head[i]=0;
		for(int i=1;i<=n;++i)d[i].id=i;
		for(int i=1;i<=n;++i)dep[i]=0;
		for(int i=1;i<=n;++i)flag[i]=0;
		for(int i=1;i<=n;++i)f[i].fa=f[i].l=f[i].r=i,f[i].maxd=0;

		for(int i=1;i<n;++i){
			int u,v,w;scanf("%d%d%d",&u,&v,&w);
			add(u,v,w);add(v,u,w);
		}
		
		dfs(1);
		fa[1][0]=1;
		for(int j=1;j<=20;++j)
			for(int i=1;i<=n;++i){
				fa[i][j]=fa[fa[i][j-1]][j-1];
				len[i][j]=len[i][j-1]+len[fa[i][j-1]][j-1];
			}
		
		sort(d+1,d+n+1,cmp);
		ll ans=0;
		for(int i=1;i<=n;++i)flag[d[i].id]=1,ans=max(work(d[i].id)*d[i].val,ans);
		printf("%lld\n",ans);
	}

	return 0;
}

C. 玫瑰花精

距离正解那么近又那么远。

线段树,类似山海经,左右边界特判!特判!

\(lxhcr\)大佬强烈推荐打平衡树

线段树+恶心码风
#include <cstring>
#include <algorithm>
#include <queue>
#include <cstdio>

using namespace std;
const int maxn=200005;
int rem[1000005];
struct node{
	int prer,nxtl,mdl,mdr;
	long long sum,pre,nxt,md;
};
int n,m;
struct tree{
	node t[maxn<<2|1];
	void push_up(int x){
		int ls=x<<1,rs=x<<1|1;
		t[x].sum=t[ls].sum+t[rs].sum;
		if(t[ls].pre>=t[rs].pre+t[ls].sum){
			t[x].pre=t[ls].pre;t[x].prer=t[ls].prer;
		}else{
			t[x].pre=t[ls].sum+t[rs].pre;t[x].prer=t[rs].prer;
		}
		
		if(t[ls].nxt+t[rs].sum>=t[rs].nxt){
			t[x].nxt=t[ls].nxt+t[rs].sum;t[x].nxtl=t[ls].nxtl;
		}else{
			t[x].nxt=t[rs].nxt;t[x].nxtl=t[rs].nxtl;
		}

		t[x].md=t[ls].nxt+t[rs].pre;t[x].mdl=t[ls].nxtl;t[x].mdr=t[rs].prer;
		if(((t[ls].md-1)>>1)>=((t[x].md-1)>>1)){
			t[x].md=t[ls].md;t[x].mdl=t[ls].mdl;t[x].mdr=t[ls].mdr;
		}
		if(((t[rs].md-1)>>1)>((t[x].md-1)>>1)){
			t[x].md=t[rs].md;t[x].mdl=t[rs].mdl;t[x].mdr=t[rs].mdr;
		}
	}
	void built(int x,int l,int r){
		if(l==r){
			t[x].md=t[x].nxt=t[x].pre=t[x].sum=1;
			t[x].mdr=t[x].nxtl=t[x].prer=t[x].mdl=l;
			return;
		}
		int mid=(l+r)>>1;
		built(x<<1,l,mid);
		built(x<<1|1,mid+1,r);
		push_up(x);
	}
	void modify(int x,int l,int r,int pos,int val){
		if(l==r){
			t[x].nxt=t[x].pre=t[x].md=t[x].sum=val;
			return;
		}
		int mid=(l+r)>>1;
		if(pos<=mid)modify(x<<1,l,mid,pos,val);
		else modify(x<<1|1,mid+1,r,pos,val);
		push_up(x);
	}
	int pos(){
		long long ans=0,mx=(t[1].md-1)/2;
		ans=t[1].mdl+mx;
		if(t[1].mdr==n)ans=n,mx=t[1].md;
		if(t[1].mdl==1)ans=1,mx=t[1].md;
		if(t[1].nxt-1>mx)ans=n,mx=t[1].nxt;
		if (t[1].pre-1>=mx)ans=1,mx=t[1].pre;
		return ans;
	}
}T;
int main(){
	scanf("%d%d",&n,&m);
	T.built(1,1,n);
	for(int i=1;i<=m;++i){
		int op,x;
		scanf("%d%d",&op,&x);
		if(op&1){
			int pos=T.pos();
			printf("%d\n",pos);
			rem[x]=pos;
			T.modify(1,1,n,pos,-maxn);
		}else{
			T.modify(1,1,n,rem[x],1);
			rem[x]=0;
		}
	}
	return 0;
}

posted @ 2022-04-09 20:27  Chen_jr  阅读(27)  评论(0编辑  收藏  举报