1. 程式人生 > 其它 >NOIP提高組模擬賽21

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;
}