noip模擬26[腎炎黃·醬累黃·換莫黃]

fengwu2005發表於2021-07-28

\(noip模擬26\;solutions\)

這個題我做的確實是得心應手,為啥呢,因為前兩次考試太難了

T1非常的簡單,只不過我忘記了一個定理,

T2就是一個小小的線段樹,雖然吧我曾經說過我再也不寫樹狀陣列了,但是我看見最長上升子序列就興奮了

碼了個樹狀陣列就溜走了,用時僅為10min,60pts,組合拳

T3我就是nb

\(T1\;神炎皇\)

這個就是一個小小的推式子,讓我給你推一推

\(a+b\,|\, a*b\),設\(d=gcd(a,b),x=a/d,y=b/d\),所以x和y互質

我們有\(k*(x+y)\,|\,x*y*d^2<====>(x+y)\,|\,x*y*d\)

因為x,y互質所以(x+y)| d,設(x+y)=k

\(k*d<=n並且k\,|\,d並且k<{d}\),所以這樣的d一共有\(\frac{n}{k^2}\)

這種x與y互質,x+y為定值,你發現x與x+y也是互質的,所以每一個k對應的x+y都有\(\phi(k)\)

k的範圍是\(\sqrt{n}\)

那麼接下來的事就只剩下線性篩了;

AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define ll long long
const int N=1e7+5;
ll n,ans,sn;
int p[N],cnt,f[N];
bool vis[N];
signed main(){
	scanf("%lld",&n);
	sn=sqrt(n);
	//memset(vis,1,sizeof(vis));
	//cout<<vis[1]<<endl;
	for(re i=2;i<=sn;i++){
		if(!vis[i])p[++cnt]=i,f[i]=i-1;
		for(re j=1;j<=cnt&&p[j]*i<=sn;j++){
			vis[p[j]*i]=true;
			if(i%p[j]==0){
				f[i*p[j]]=f[i]*p[j];
				break;
			}
			else f[i*p[j]]=f[i]*f[p[j]];
		}
	}
	for(re i=1;i<=sn;i++){
		ans+=f[i]*(n/i/i);
	}
	printf("%lld",ans);
}

\(T2\;降雷皇\)

這個題真的是簡單極了,我也沒想到會有這麼簡單的題,打了個60分的組合拳就走了

最後發現這個用線段樹維護一下就好了

畢竟是動態開點,而且這個電阻值只有1e5,完全不需要離散化,

對於每一種長度也就是最長上升子序列的長度建立一個線段樹,

線段樹的下標是電阻值,因為你查詢的時候是按照電阻值來查詢的

線段樹中維護的權值即為方案數,這樣遍歷一遍這個序列,\(O(nlogn)\)就可以解決方案數了

最後直接輸出最長長度的線段樹的所有權值加和,說白了就是根節點的sum

AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int
const int N=1e5+5;
const int mod=123456789;
int n,typ,ans1,ans2;
int omu[N],f[N],sum[N];
struct SZSZ{
	int tr[N];
	int lb(int x){return x&(-x);};
	void ins(int x,int v){
		for(re i=x;i<=1e5;i+=lb(i))
			tr[i]=max(tr[i],v);
	}
	int query(int x){
		int ret=0;
		for(re i=x;i;i-=lb(i))
			ret=max(ret,tr[i]);
		return ret;
	}
}sz;
int dp[N];
struct XDS{
	int ls[N*80],rs[N*80];
	int sum[N*80];
	int seg;
	void pushup(int x){
		sum[x]=(sum[ls[x]]+sum[rs[x]])%mod;
		return ;
	}
	void ins(int &x,int l,int r,int pos,int v){
		if(!x)x=++seg;
		if(l==r){
			sum[x]=(sum[x]+v)%mod;
			return ;
		}
		int mid=l+r>>1;
		if(pos<=mid)ins(ls[x],l,mid,pos,v);
		else ins(rs[x],mid+1,r,pos,v);
		pushup(x);return ;
	}
	int query(int x,int l,int r,int ql,int qr){
		if(!x)return 0;
		if(ql<=l&&r<=qr)return sum[x];
		int mid=l+r>>1,ret=0;
		if(ql<=mid)ret=(ret+query(ls[x],l,mid,ql,qr))%mod;
		if(qr>mid)ret=(ret+query(rs[x],mid+1,r,ql,qr))%mod;
		return ret;
	}
}xds;
int rt[N];
signed main(){
	scanf("%d%d",&n,&typ);
	for(re i=1;i<=n;i++){
		scanf("%d",&omu[i]);
		dp[i]=sz.query(omu[i]-1)+1;
		ans1=max(ans1,dp[i]);
		sz.ins(omu[i],dp[i]);
		//cout<<omu[i]<<" "<<dp[i]<<endl;
	}
	printf("%d\n",ans1);
	if(typ==1){
		int R=1e5+1,mn=0x3f3f3f3f;
		for(re i=1;i<=n;i++){
			int tmp=xds.query(rt[dp[i]-1],1,R,1,omu[i]-1);
			if(mn>=omu[i]&&tmp==0)tmp=1,mn=omu[i];
			xds.ins(rt[dp[i]],1,R,omu[i],tmp);
		}
		printf("%d",xds.query(rt[ans1],1,R,1,R));
	}
}

\(T3\;幻魔皇\)

這整一棵樹都充斥這非波那契數列,真的就直接倒就好了

其實還有更簡單的\(O(n^2)\)做法還比我的簡單,比我的快

去看別人的部落格吧,我這個非常難理解,

主要思路就是,首先列舉每一層,每一層的黑色節點和白色節點的型別都是一樣的

黑色節點直接列舉左右兒子就行,白色節點就是直接用自己連線自己子樹中的白色節點

注意黑色子樹中有一個不符合規律的,就是這個黑色節點的白兒子,把這個拎出來單獨算

O(n^3)
#include<bits/stdc++.h>
using namespace std;
#define re register int
const int N=5e3+5;
const int mod=123456789;
int fb[N],ans[N*2],n;
signed main(){
	scanf("%d",&n);
	fb[1]=1;fb[2]=1;
	for(re i=3;i<=5000;i++)
		fb[i]=(fb[i-1]+fb[i-2])%mod;
	for(re i=2;i<=n;i++){
		for(re j=i+2;j<=n;j++){
			for(re k=i+3;k<=n;k++){
				ans[j-i+k-i]+=1ll*fb[i-1]*fb[j-i-1]%mod*fb[k-i-2]%mod;
				ans[j-i+k-i]%=mod;
			}
			ans[j-i+1]+=1ll*fb[i-1]*fb[j-i-1]%mod;
			ans[j-i+1]%=mod;
		}
		for(re j=i+2;j<=n;j++){
			ans[j-i]+=1ll*fb[i-2]*fb[j-i-1]%mod;
			ans[j-i]%=mod;
		}
	}
	for(re j=3;j<=n;j++){
		ans[j-1]+=fb[j-2];
		ans[j-1]%=mod;
	}
	for(re i=1;i<=2*n;i++)printf("%d ",ans[i]);
}

·

AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int
const int N=5e3+5;
const int mod=123456789;
int fb[N],ffb[N][N*2],ans[N*2],n;
signed main(){
	scanf("%d",&n);
	fb[1]=1;fb[2]=1;
	for(re i=3;i<=5000;i++)
		fb[i]=(fb[i-1]+fb[i-2])%mod;
	for(re i=2;i<=n;i++){
		for(re j=1;j<=2*n;j++)
			ffb[i][j]=(ffb[i][j]+ffb[i-1][j])%mod;
		for(re j=3;j<=n;j++){
			//if(i==4&&j==3)cout<<ffb[j][i+j]<<" "<<i+j<<endl;
			ffb[max(j,i)][i+j]+=1ll*fb[i-1]*fb[j-2]%mod;
			ffb[max(j,i)][i+j]%=mod;
			//if(i==4&&j==3)cout<<ffb[j][i+j]<<endl;
		}
	}
	//cout<<ffb[4][7]<<endl;
	for(re i=2;i<=n;i++){
		for(re j=5;j<=2*(n-i);j++){
			ans[j]+=1ll*fb[i-1]*ffb[n-i][j]%mod;
			ans[j]%=mod;
		}
		for(re j=i+2;j<=n;j++){
			ans[j-i+1]+=1ll*fb[i-1]*fb[j-i-1]%mod;
			ans[j-i+1]%=mod;
		}
		for(re j=i+2;j<=n;j++){
			ans[j-i]+=1ll*fb[i-2]*fb[j-i-1]%mod;
			ans[j-i]%=mod;
		}
	}
	for(re j=3;j<=n;j++){
		ans[j-1]+=fb[j-2];
		ans[j-1]%=mod;
	}
	for(re i=1;i<=2*n;i++)printf("%d ",ans[i]);
}