1. 程式人生 > 其它 >Noip模擬87 2021.11.1

Noip模擬87 2021.11.1

T1 集合均值 T2 聚烷撐乙二醇 T3 技術情報局 T4 肯德基

T1 集合均值

打了一個比較穩的\(70pts\),本地測極限資料跑了\(0.2s\),然後\(Waitingcoders\)上面\(T\)成了\(35\),究極疑惑

其實和正解就差在了一個線性複雜度的求逆元,因為我的複雜度是\(O(nmlog(nm))\)的。。。

那麼考場上推了推發現個規律

就是說他的所有情況不要按照每種情況一一計算貢獻,要按照所有情況的每一列計算貢獻,

這樣你會發現每一列的答案是公差為\(p[1]\)的等差數列,其中\(p[1]\)即等差數列的第一項,他等於\(sum(nm)\times (nm-1)!\)

然後這不是答案,而是你計算每一列的總和,然後你再除以一個\(A\)集合的大小就算出平均數了,然後最後再除以總方案數\(nm!\)

就是答案

mos
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int NN=2e7+5,mod=998244353;
namespace AE86{
	auto read=[](){
		int x=0,f=1;char ch=getchar();
		while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
		while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
		return x*f;
	};
	auto write=[](int x,char opt='\n'){
		char ch[20];short len=0;if(x<0)x=~x+1,putchar('-');
		do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x);
		for(short i=len-1;i>=0;--i)putchar(ch[i]);putchar(opt);
	};
	inline int qmo(int a,int b,int ans=1){
		int c=mod;for(;b;b>>=1,a=a*a%c)if(b&1)ans=ans*a%c;
		return ans;
	}
}using namespace AE86;
int n,m,a[100005],ans,T,tmp;
int h[NN],v[NN];
inline void pre(){
	h[0]=h[1]=1; v[0]=v[1]=1;
	for(int i=2;i<=m*n+1;++i)h[i]=h[i-1]*i%mod;
	tmp=qmo(h[n*m],mod-2); v[n*m+1]=qmo(h[n*m+1],mod-2);
	for(int i=n*m;i>=2;--i) v[i]=v[i+1]*(i+1)%mod;
}
namespace WSN{
	inline short main(){
		freopen("mos.in","r",stdin);
		freopen("mos.out","w",stdout);
		n=read(); m=read(); pre();
		for(int i=1;i<=n;++i) a[i]=read(),T+=a[i];
		T%=mod; T=T*m%mod;
		T=T*h[n*m-1]%mod;
		for(int i=1;i<=n*m;++i)
			ans=(ans+T*i%mod*v[i+1]%mod*h[i]%mod)%mod;
		ans=ans*tmp%mod;
		write(ans);
		return 0;
	}
}
signed main(){return WSN::main();}

T2 聚烷撐乙二醇

考場上讀錯題了以為第一個部分分所有的\(l=r\)並且等於同一個數,然後就直接輸出了第一項,然後爆蛋了

然而他不相等啊!!那麼,直接輸出最大值就好了

考慮正解,不難發現在一段區間隨機選擇一個數的貢獻是\(\frac{l+r}{2}\)

那麼我們記每一個生成器的貢獻為\(d_i\),然後設\(f_i\)表示從第\(i\)個開始遊戲的最大期望

他這種設法有些模糊,我覺得就是表示一個答案,他這麼說的就好像是從哪裡開始遊戲是隨便選擇的一樣

不難發現最後的答案\(f_n\)就是\(d_n\),那麼可以倒著轉移,分三種情況轉移

\(f_{n-1}= \begin{cases} d_{n-1} &\mathrm{ if }L_{n-1}>f_n\\ f_n &\mathrm{ if }R_{n-1}<f_n\\ \frac{f_n-L_{n-1}}{R_{n-1}-L_{n-1}}\times f_n &\mathrm{ if }x<f_n\\ \frac{R_{n-1}-f_n}{R_{n-1}-L_{n-1}}\times(\frac{f_n+R_{n-1}}{2}) &\mathrm{ if }x\geq f_n \end{cases}\)

如果\(f_{i+1}\)沒有落在當前考慮的\([L_i,R_i]\)區間上,直接選擇較優的轉移即可

如果在\([L_i,R_i]\)區間上,考慮第\(i\)個生成器生成的數\(X\)\(f_{i+1}\)的大小關係合併轉移即可

pag
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int NN=1e6+5;
namespace AE86{
	auto read=[](){
		int x=0,f=1;char ch=getchar();
		while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
		while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
		return x*f;
	};
	auto write=[](int x,char opt='\n'){
		char ch[20];short len=0;if(x<0)x=~x+1,putchar('-');
		do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x);
		for(short i=len-1;i>=0;--i){putchar(ch[i]);}putchar(opt);
	};
}using namespace AE86;
typedef long double D;
int n,tmp;
D f[NN],d[NN];
struct node{D l,r;}s[NN];
namespace WSN{
	inline short main(){
		freopen("pag.in","r",stdin);
		freopen("pag.out","w",stdout);
		n=read();
		for(int i=1;i<=n;i++){
			s[i].l=read(),s[i].r=read();
			d[i]=(s[i].l+s[i].r)/2;
			if(s[i].l==s[i].r) ++tmp;
		}
		if(tmp==n){
			int ans=0;
			for(int i=1;i<=n;i++)ans=max(ans,(int)s[i].l);
			printf("%.5lf\n",1.0*ans);
			return 0;
		}
		f[n]=d[n];
		for(int i=n-1;i;i--){
			if(f[i+1]<s[i].l){
				f[i]=d[i];
			}else if(f[i+1]>s[i].r){
				f[i]=f[i+1];
			}else{
				f[i]=(s[i].r-f[i+1])/(s[i].r-s[i].l)*(f[i+1]+s[i].r)/2;
				f[i]+=(f[i+1]-s[i].l)/(s[i].r-s[i].l)*f[i+1];
			}
		}
		printf("%.5Lf\n",f[1]);
		return 0;
	}
}
signed main(){return WSN::main();}

T3 技術情報局

比較明顯是要建出大根堆笛卡爾樹的,不過在\(T1\)花時間太多而且還想做\(T4\)於是就直接打了一個部分分和一個線段樹走了

預計得分\(40pts\),實際得分\(20\),因為卡了\(O(n)\)的常,沒事,正常正常。。。。。

考慮建出笛卡爾樹後如何合併答案,想到原來做過的一道題斐波

沒錯還是他,我好像已經兩次在別的部落格裡面拿出這套題的連結了

這道題的維護方式和他一樣,那麼我們只需要記錄四個值就可以合併區間答案了,時間複雜度\(O(n)\)

tio
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int NN=1e7+5;
namespace AE86{
	auto read=[](){
		int x=0,f=1;char ch=getchar();
		while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
		while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
		return x*f;
	};
	auto write=[](int x,char opt='\n'){
		char ch[20];short len=0;if(x<0)x=~x+1,putchar('-');
		do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x);
		for(short i=len-1;i>=0;--i){putchar(ch[i]);}putchar(opt);
	};
	auto max_=[](int a,int b){return a>b?a:b;};
	auto min_=[](int a,int b){return a<b?a:b;};
}using namespace AE86;
int n,s,l,r,mod,w[NN],ans;
vector<int> sca;
namespace GenHelper {
	unsigned z1, z2, z3, z4, b;
	unsigned rand_() {
	b = ((z1 << 6) ^ z1) >> 13;
	z1 = ((z1 & 4294967294U) << 18) ^ b;
	b = ((z2 << 2) ^ z2) >> 27;
	z2 = ((z2 & 4294967288U) << 2) ^ b;
	b = ((z3 << 13) ^ z3) >> 21;
	z3 = ((z3 & 4294967280U) << 7) ^ b;
	b = ((z4 << 3) ^ z4) >> 12;
	z4 = ((z4 & 4294967168U) << 13) ^ b;
	return (z1 ^ z2 ^ z3 ^ z4);
	}
}
vector<int> get (int n, unsigned s, int l, int r) {
	vector<int> a;
	using namespace GenHelper;
	z1 = s;
	z2 = unsigned((~s) ^ 0x233333333U);
	z3 = unsigned(s ^ 0x1234598766U);
	z4 = (~s) + 51;
	for (int i = 1; i <= n; i ++) {
		int x = rand_() & 32767;
		int y = rand_() & 32767;
		a.push_back(l + (x * 32768 + y) % (r - l + 1));
	}
	return a;
}
inline int qmo(int a,int b,int ans=1){
	int c=mod;for(;b;b>>=1,a=a*a%c)if(b&1)ans=ans*a%c;
	return ans;
}
namespace TASK{
	int pw[NN];
	inline void task(){
		int ans=0;pw[0]=1;for(int i=1;i<=n;++i)pw[i]=w[i]*pw[i-1]%mod;
		for(int i=1;i<=n;++i)ans=(ans+pw[i]*w[1]%mod*(n-i+1)%mod)%mod;
		write(ans);
	}
}using namespace TASK;
namespace carisian_tree{
	int son[NN][2],siz[NN],stk[NN],top;
	inline void build(int *a){
		for(int i=1;i<=n;i++){
			while(top&&a[stk[top]]<a[i]) son[i][0]=stk[top--];
			if(top) son[stk[top]][1]=i;
			stk[++top]=i;
		}
	}
	struct node{
		int l,r,sum,mul;
		node operator+(const node&x)const{
			node ret;ret.l=ret.r=ret.sum=ret.mul=0;
			ret.sum=(sum+x.sum+x.l*r%mod)%mod;
			ret.l=(l+mul*x.l%mod)%mod;
			ret.r=(x.r+x.mul*r%mod)%mod;
			ret.mul=mul*x.mul%mod;
			return ret;
		}
	}dp[NN<<2];
	inline void Dfs(int x){
		dp[x]=node{w[x],w[x],w[x],w[x]};
		if(son[x][0]) Dfs(son[x][0]),dp[x]=dp[son[x][0]]+dp[x];
		if(son[x][1]) Dfs(son[x][1]),dp[x]=dp[x]+dp[son[x][1]];
		ans=(ans+(dp[x].sum-dp[son[x][0]].sum-dp[son[x][1]].sum+mod+mod)%mod*w[x]%mod)%mod;
	}
}using namespace carisian_tree;
namespace WSN{
	inline short main(){
		FILE *wsn=freopen("tio.in","r",stdin);
		wsn=freopen("tio.out","w",stdout);
		n=read();s=read();l=read();r=read();mod=read();
		sca=get(n,s,l,r); int sz=sca.size();
		for(int i=0;i<sz;++i) w[i+1]=sca[i];
		if(l==r) return task(),0; build(w);
		Dfs(stk[1]);
		write(ans);
		return 0;
	}
}
signed main(){return WSN::main();}

T4 肯德基

莫比烏斯大白板

考場上推杜教篩的部分分用時:\(\leq 1h\)

考場上推正解用時:\(\leq 15min\)

大霧

然而考試的時候不會解決等差數列的\(/2\)要特判的問題導致沒有調出來正解程式碼,於是。。。

然後就gg。。。。。。

非常鬱悶,啊啊啊啊啊!!!!

問題實際上是求範圍在\([1,n]\)內的沒有平方質因子的數的總和

考慮容斥

我們列舉平方因子是哪個,最後用\(sum(n)\)減去有平方因子的數的和就是答案

那麼答案就是

\(\begin{matrix} & sum(n)-\sum\limits_{d=2}^{\sqrt{n}}\sum\limits_{i=1}^{\frac{n}{d^2}}id^2 \\ & =sum(n)-\sum\limits_{d=2}^{\sqrt{n}}d^2\sum\limits_{i=1}^{\frac{n}{d^2}}i \\ & =sum(n)-\sum\limits_{d=2}^{\sqrt{n}}d^2sum(\frac{n}{d^2}) \end{matrix}\)

但其實他是不對的!!!

為什麼?因為會算重。

在哪裡?在一個數有多個質因子的地方,於是我們加上容斥係數\(\mu\),他就對了,即

\(=sum(n)-\sum\limits_{d=2}^{\sqrt{n}}\mu(d)d^2sum(\frac{n}{d^2})\)

然後發現\(sum(n)\)可以直接放進大迴圈裡算,那麼答案就是

\(ans=\sum\limits_{d=1}^{\sqrt{n}}\mu(d)d^2sum(\frac{n}{d^2})\)

然後直接篩出\(\mu(d)d^2\)的字首和數論分塊即可

可能是昨天學了一天的杜教篩和莫比烏斯反演,這題確實比較白板

\(\huge{另外,如果有路過的大佬會杜教篩的部分分,請瘋狂在評論區D我}\)

kfc
#include<bits/stdc++.h>
#define int unsigned long long
using namespace std;
const int NN=10000001;
namespace AE86{
	auto read=[](){
		int x=0,f=1;char ch=getchar();
		while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
		while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
		return x*f;
	};
	auto write=[](int x,char opt='\n'){
		char ch[20];short len=0;if(x<0)x=~x+1,putchar('-');
		do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x);
		for(short i=len-1;i>=0;--i)putchar(ch[i]);putchar(opt);
	};
}using namespace AE86;
int T,n;
signed prime[NN],len;
signed mu[NN];
bool vis[NN];
int F[NN],S[NN];
inline void getprime(){
	mu[1]=1; F[1]=1; S[1]=1;
	for(int i=2;i<NN;++i){
		if(!vis[i]) prime[++len]=i,mu[i]=-1;
		for(int j=1;j<=len&&prime[j]*i<NN;++j){
			vis[i*prime[j]]=1;
			if(i%prime[j]==0)break;
			mu[i*prime[j]]=-mu[i];
		}
		F[i]=(F[i-1]+mu[i]*mu[i]*i);
		S[i]=(S[i-1]+i*i*mu[i]);
	}
}
int ans,m;
auto id=[](int x){return n/x;};
inline int sig(int n){return ((n+1)&1) ? ((n/2)*(n+1)):((n+1)/2*n);}
auto solve=[](){
	n=read();
	if(n<=1e7) return write(F[n]),void();
	ans=0; m=sqrt(n);
	for(int l=1,r;l<=m;l=r+1){
		r=max(l,(int)sqrt(n/(n/l/l)));
		ans+=(S[r]-S[l-1])*sig(n/l/l);
	}
	write(ans);
};
namespace WSN{
	inline short main(){
		freopen("kfc.in","r",stdin);
		freopen("kfc.out","w",stdout);
		T=read(); getprime();
		while(T--) solve();
		return 0;
	}
}
signed main(){return WSN::main();}