1. 程式人生 > 其它 >noip模擬72

noip模擬72

noip模擬72

考場

早上去上了語文課,於是回來選擇只寫兩道題
\(t1\) 看錯了題沒看見只能一個一個往裡加於是主動棄掉
\(t2\) 看起來像是一個二維樹狀陣列就可以做的題(然而沒有意識到模數不互質的嚴重性)
\(t3\) 以為有單調性二分加雜湊就可以了,於是先寫 \(t3\),寫完才發現問題所在,看起來像是字尾類的科技,直接放棄
\(t4\) 沒看


A. T1 出了個大陰間題

一看範圍是狀壓,數是一個一個往裡加的,這就提供一個很好的性質:與 \(b\) 產生過程無關,其貢獻可以直接計算
\(f[S][i]\) 表示已選集合為 \(S\),且 \(a\) 的值為 \(j\)
顯然開不下,然而發現性質只可能比集合 \(max\)

\(1\) 或相等,所以開兩維即可
分別記錄真實的 \(a\) 值,排列方案數,以及此時總和

點選檢視程式碼
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=1e6+5;
const int mod=1e9+7;
const int maxm=40;
int n,a[maxm],all,m,val[maxm],sum[maxm],ans;
int f[maxn][maxm];//值
int g[maxn][maxm];//排列個數
int h[maxn][maxm];//和
int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
void modadd(int &a,int b){
	a=(a+b)%mod;
}
signed main(){
	freopen("repair.in","r",stdin);
	freopen("repair.out","w",stdout);
	n=read();m=read();
	for(int i=1;i<=n;i++)a[i]=read();
	for(int i=1;i<=n;i++)val[i]=val[i-1]*2+1,sum[i]=sum[i-1]+val[i];
	all=(1<<n)-1;
	for(int i=1;i<=n;i++)f[1<<(i-1)][0]=a[i],g[(1<<(i-1))][0]=1;
	for(int i=1;i<=all;i++){
		if(!f[i][0])continue;
		for(int j=1;j<=n;j++){
			if((1<<(j-1))&i)continue;
			int S=(i|(1<<(j-1)));
			for(int k=0;k<=1;k++){
				if(!f[i][k])continue;
				int nxt=(f[i][k]==a[j])?a[j]+1:max(f[i][k],a[j]);
//				cout<<nxt<<endl;
				if(nxt<f[S][0]){
					if(nxt>f[S][1])f[S][1]=nxt,g[S][1]=h[S][1]=0;
					if(nxt==f[S][1]){
						modadd(g[S][1],g[i][k]);
						modadd(h[S][1],nxt*g[i][k]%mod+h[i][k]);
					}
				}
				else{
					if(nxt>f[S][0]){
						f[S][1]=f[S][0],g[S][1]=g[S][0],h[S][1]=h[S][0];
						f[S][0]=nxt,g[S][0]=h[S][0]=0;
					}
					if(nxt==f[S][0]){
						modadd(g[S][0],g[i][k]);
						modadd(h[S][0],nxt*g[i][k]%mod+h[i][k]);
					}
				}
			}
//			cout<<f[S][0]<<" "<<g[S][0]<<" "<<h[S][0]<<endl;
		}
	}
	ans=(h[all][0]*m%mod+g[all][0]*sum[n-2]%mod)%mod;
	cout<<f[all][0]<<" "<<ans;
	return 0;
}

B. T2 最簡單辣快來做

由於模數不互質,不能提前預處理負冪次
考慮進行二維字首和
把關鍵點形成的網格拿出來處理四個方向的字首和,然後將詢問點推到網格整點進行加減計算
注意預處理次方,否則得寫光速冪

程式碼實現
#include<bits/stdc++.h>
using namespace std;
const int maxn=3005;
int n,t,ll,rr,mod,a,b,totx,toty,x[maxn],y[maxn],sum[maxn][maxn][4],h[maxn],px[maxn],py[maxn],xx,yy,lx[maxn],ly[maxn];
int pos[maxn][maxn];
int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
void modadd(int &a,int b){
	a=(a+b)%mod;
	return ;
}
int po(int a,int b){
	if(b<0)return 0;
	int ans=1;
	while(b){
//		cout<<a<<" "<<b<<endl;
		if(b&1)ans=1ll*ans*a%mod;
		a=1ll*a*a%mod;
		b>>=1;
	}
	return ans;
}
int main(){
	freopen("satellite.in","r",stdin);
	freopen("satellite.out","w",stdout);
	n=read(),t=read(),ll=read(),rr=read(),mod=read(),a=read(),b=read();
	for(int i=1;i<=n;i++){
		h[i]=read();
		lx[i]=x[i]=read();
		ly[i]=y[i]=read();
	}
	lx[n+1]=ll,ly[n+1]=rr;
	sort(lx+1,lx+n+2);totx=unique(lx+1,lx+n+2)-lx-1;
	sort(ly+1,ly+n+2);toty=unique(ly+1,ly+n+2)-ly-1;
	for(int i=1;i<=n;i++){
		x[i]=lower_bound(lx+1,lx+totx+1,x[i])-lx;
		y[i]=lower_bound(ly+1,ly+toty+1,y[i])-ly;
		modadd(pos[x[i]][y[i]],h[i]);
	}
	for(int i=1;i<=totx+1;i++)px[i]=po(a,lx[i]-lx[i-1]);
	for(int i=1;i<=toty+1;i++)py[i]=po(b,ly[i]-ly[i-1]);
//	cout<<"hhh "<<endl;
	for(int i=1;i<=totx;i++){
		for(int j=1;j<=toty;j++){
			int res=0;
			modadd(res,1ll*sum[i-1][j][0]*px[i]%mod);
			modadd(res,1ll*sum[i][j-1][0]*py[j]%mod);
			modadd(res,mod-1ll*sum[i-1][j-1][0]*px[i]%mod*py[j]%mod);
			modadd(res,pos[i][j]);sum[i][j][0]=res;
		}
	}
	for(int i=1;i<=totx;i++){
		for(int j=toty;j>=1;j--){
			int res=0;
			modadd(res,1ll*sum[i-1][j][1]*px[i]%mod);
			modadd(res,1ll*sum[i][j+1][1]*py[j+1]%mod);
			modadd(res,mod-1ll*sum[i-1][j+1][1]*px[i]%mod*py[j+1]%mod);
			modadd(res,pos[i][j]);sum[i][j][1]=res;
		}
	}
	for(int i=totx;i>=1;i--){
		for(int j=1;j<=toty;j++){
			int res=0;
			modadd(res,1ll*sum[i+1][j][2]*px[i+1]%mod);
			modadd(res,1ll*sum[i][j-1][2]*py[j]%mod);
			modadd(res,mod-1ll*sum[i+1][j-1][2]*px[i+1]%mod*py[j]%mod);
			modadd(res,pos[i][j]);sum[i][j][2]=res;
		}
	}
	for(int i=totx;i>=1;i--){
		for(int j=toty;j>=1;j--){
			int res=0;
			modadd(res,1ll*sum[i+1][j][3]*px[i+1]%mod);
			modadd(res,1ll*sum[i][j+1][3]*py[j+1]%mod);
			modadd(res,mod-1ll*sum[i+1][j+1][3]*px[i+1]%mod*py[j+1]%mod);
			modadd(res,pos[i][j]);sum[i][j][3]=res;
		}
	}
//	for(int i=0;i<=3;i++)cout<<sum[2][2][i]<<" ";
//	puts("");
	while(t--){
		xx=read(),yy=read();
		int posx=lower_bound(lx+1,lx+totx+1,xx)-lx;
		int posy=lower_bound(ly+1,ly+toty+1,yy)-ly;
		int pox1=po(a,xx-lx[posx-1]),pox2=po(a,lx[posx]-xx),poy1=po(b,yy-ly[posy-1]),poy2=po(b,ly[posy]-yy),res=0;
		modadd(res,1ll*pox1*poy1%mod*sum[posx-1][posy-1][0]%mod);
		modadd(res,1ll*pox1*poy2%mod*sum[posx-1][posy][1]%mod);
		modadd(res,1ll*pox2*poy1%mod*sum[posx][posy-1][2]%mod);
		modadd(res,1ll*pox2*poy2%mod*sum[posx][posy][3]%mod);
		printf("%d\n",res);
	}
	return 0;
}

C. T3 是我的你不要搶

考慮根號分治,大於 \(\sqrt n\) 不超過 \(\sqrt n\) ,可以兩兩處理的總複雜度是 \(n\sqrt n\) 的,記憶化一下
其餘的單次 \(\sqrt n\) 處理即可


D. T4 顯然也是我整的

神仙題,下次碰見還想不出來……
搬篇巨佬的題解:

其實不用用 \(set\),直接小根堆維護即可

程式碼實現
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=2e5+5;
int n,m,x,sta[maxn],tp,t;
int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
priority_queue<int,vector<int>,greater<int> >q;
int gcd(int a,int b){return a==0?b:gcd(b%a,a);}
int solve(int n){
	int res=0,g=0;
	if(q.top()>n/2){
		int nxt=2*q.top()-n;
		n-=nxt;res+=nxt;
		while(!q.empty())sta[++tp]=q.top(),q.pop();
		while(tp)q.push(sta[tp--]-nxt);
	}
	while(!q.empty()&&q.top()<=n/2){
		g=gcd(g,q.top());
		q.pop();
	}
	while(!q.empty()&&q.top()+g<=n){
		g=gcd(g,q.top());q.pop();
	}
	if(q.empty())return res+g;
	int ans=g+n%g;
	while(!q.empty())sta[++tp]=q.top(),q.pop();
	while(tp)q.push(sta[tp--]+ans-n);
	q.push(g);
	return res+solve(ans);
}
signed main(){
	freopen("graph.in","r",stdin);
    freopen("graph.out","w",stdout);
	t=read();
	while(t--){
		n=read(),m=read();
		for(int i=1;i<=m;i++)x=read(),q.push(x);
		printf("%lld\n",solve(n));
	}
	return 0;
}