【比賽】CSP提高組模擬1

萝卜甜了發表於2024-07-14

和初三學長們一起打的比賽,被人家爆殺了。

T1 最短路 20Pts

原題 Cow Toll Paths G

正解是按點權排序後跑一遍 Floyd,歪解是多迭代幾遍。

賽時沒開 long long \(80 \to 20\)

點選檢視程式碼
#include<bits/stdc++.h>
#define int ll 
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=305;
pii a[N][N];
int n,m,v[N],x,y,q,s,t,w;
main(){
	freopen("path.in","r",stdin);
	freopen("path.out","w",stdout); 
	cin>>n>>m>>q;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(i==j)a[i][j]={0,0};
			else a[i][j]={0x3f3f3f3f3f3f3f3f,0};
		}
	}
	for(int i=1;i<=n;i++)cin>>v[i];
	for(int i=1;i<=m;i++){
		cin>>x>>y>>w;
		w=min(w,a[x][y].first);
		a[x][y].first=a[y][x].first=w;
		a[x][y].second=a[y][x].second=max(v[x],v[y]);
	}
	int T=3;
	while(T--)
	for(int k=1;k<=n;k++){
		for(int i=1;i<=n;i++){
			if(i==k)continue;
			for(int j=1;j<=n;j++){
				if(j==k||i==j)continue;
				if(a[i][j].first+a[i][j].second>a[i][k].first+a[k][j].first+max(a[i][k].second,a[k][j].second)){
					a[i][j].first=a[i][k].first+a[k][j].first;
					a[i][j].second=max(a[i][k].second,a[k][j].second);
				}
			}
		}
	}
	while(q--){
		cin>>x>>y;
		if(a[x][y].first==0x3f3f3f3f3f3f3f3f)cout<<"-1\n";
		else cout<<a[x][y].first+a[x][y].second<<"\n";
	}
	return 0;
}

T2 方格取數 40Pts

原題 KUP-Plot purchase

首先判掉在 \([k,2k]\)(直接輸出) 和 \((2k,+\infty)\)(一定不選)的數,剩下的數就都在 \((1,k-1)\) 之間了;
此時如果我們能找出一個總和大於 \(2k\) 的矩陣,那麼一定有解,因為其一定包括了一個總和在 \([k,2k]\) 之間的矩陣。
所以我們先找出一個總和大於 \(2k\) 的極大矩陣,然後每次刪去一行,如果這一行的和大於 \(k\) 就在這一行中一個一個刪,否則繼續在大矩陣中進行。
單調棧找最大矩陣即可。

賽時想到了單調棧,但是沒打出來,隨機化 40。

點選檢視程式碼
#include<bits/stdc++.h>
#define int ll
using namespace std;
typedef long long ll;
const int N=2005;
int n,k,a[N][N],sum[N][N],l[N][N],r[N][N],s[N][N],mp[N][N];
int get(int x,int y,int xx,int yy){
	return sum[xx][yy]-sum[x-1][yy]-sum[xx][y-1]+sum[x-1][y-1];
}
main(){
	freopen("matrix.in","r",stdin);
	freopen("matrix.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	cin>>n>>k;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			cin>>a[i][j];
			if(a[i][j]>=k&&a[i][j]<=k*2){
				cout<<i<<" "<<j<<" "<<i<<" "<<j;
				return 0;
			}
			else mp[i][j]=(a[i][j]<k);
			sum[i][j]=a[i][j]+sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(mp[i][j])s[i][j]=s[i-1][j]+1;
			else s[i][j]=0;
		}
	}
	for(int i=1;i<=n;i++){
		stack<int> ss;
		for(int j=1;j<=n+1;j++){
			while(!ss.empty()&&s[i][j]<s[i][ss.top()]){
				r[i][ss.top()]=j-1;
				ss.pop();
			}
			ss.push(j);
		}
		while(!ss.empty())ss.pop();
		for(int j=n;j>=0;j--){
			while(!ss.empty()&&s[i][j]<s[i][ss.top()]){
				l[i][ss.top()]=j+1;
				ss.pop();
			}
			ss.push(j);
		}
		for(int j=1;j<=n;j++){
			int now=get(i-s[i][j]+1,l[i][j],i,r[i][j]);
			if(now>=k){
				if(now<=k*2){
					cout<<i-s[i][j]+1<<" "<<l[i][j]<<" "<<i<<" "<<r[i][j];
					return 0;
				}
				else{
					for(int h=l[i][j]+1;h<=r[i][j];h++){
						int noww=sum[i][h]-sum[i][l[i][j]-1];
						if(noww>=k&&noww<=2*k){
							cout<<i<<" "<<l[i][j]<<" "<<i<<" "<<h;
							return 0;
						}
					}
				}
			}
		}
	}
	cout<<-1;
	return 0;
}

T3 陣列 0Pts

原題 Please, another Queries on Array?

對於尤拉函式,有式子 \(\varphi(x)=x \ \cdot \ \prod_{i=1}^n\limits (\frac{p_i-1}{p_i})\),其中 \(p_i\)\(x\) 的所有質因數。

區間積可以用線段樹維護,難點在於質因子的維護。

透過一些技術手段,我們發現,在 300 以內只有 62 個質因子,剛好可以用一個 long long 來狀壓。
同樣丟到線段樹裡即可。

賽時因為 1<<62 爆零了。

1<<62 \(\to\) 1ll<<62 \(0 \to 100\)

點選檢視程式碼
#include<bits/stdc++.h>
#define int ll
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int mod=1e9+7;
const int N=4e5+5;
int n,a[N],l,r,x,q;
string op;
int prim[]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293};
inline int qp(int a,int b,int mod=mod){
	int ans=1;
	while(b){
		if(b&1)ans=ans*a%mod;
		b>>=1;
		a=a*a%mod;
	}
	return ans;
}
inline int getp(int x){
	int p=0;
	for(int i=0;i<62;i++){
		if(x%prim[i]==0)p|=(1ll<<i);
	}
	return p;
}
struct tree{
	int l,r,mul,lzm;
	int p,lzp;
	int len(){return r-l+1;}
}t[N<<2];
void pushup(int k){
	t[k].mul=t[k<<1].mul*t[k<<1|1].mul%mod;
	t[k].p=t[k<<1].p|t[k<<1|1].p;
}
void build(int k,int l,int r){
	t[k]={l,r,a[l],1};
	if(l==r){
		int p=getp(a[l]);
		t[k].p=p;t[k].lzp=0;
		return;
	}
	int mid=(l+r)>>1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
	pushup(k);
}
void pushdown(int k){
	if(t[k].lzm!=1){
		t[k<<1].mul=t[k<<1].mul*qp(t[k].lzm,t[k<<1].len())%mod;
		t[k<<1].lzm=t[k<<1].lzm*t[k].lzm%mod;
		t[k<<1|1].mul=t[k<<1|1].mul*qp(t[k].lzm,t[k<<1|1].len())%mod;
		t[k<<1|1].lzm=t[k<<1|1].lzm*t[k].lzm%mod;
		t[k].lzm=1;
	}
	if(t[k].lzp!=0){
		t[k<<1].p|=t[k].lzp;
		t[k<<1].lzp|=t[k].lzp;
		t[k<<1|1].p|=t[k].lzp;
		t[k<<1|1].lzp|=t[k].lzp;
		t[k].lzp=0;
	}
}
void update(int k,int l,int r,int val,int p){
	if(l<=t[k].l&&t[k].r<=r){
		t[k].mul=t[k].mul*qp(val,t[k].len())%mod;
		t[k].lzm=t[k].lzm*val%mod;
		t[k].p|=p;
		t[k].lzp|=p;
		return;
	}
	pushdown(k);
	int mid=(t[k].l+t[k].r)>>1;
	if(l<=mid)update(k<<1,l,r,val,p);
	if(r>mid)update(k<<1|1,l,r,val,p);
	pushup(k);
}
pii query(int k,int l,int r){
	if(l<=t[k].l&&t[k].r<=r){
		return {t[k].mul,t[k].p};
	}
	pushdown(k);
	int mid=(t[k].l+t[k].r)>>1;
	pii ans={1,0};
	if(l<=mid){
		pii L=query(k<<1,l,r);
		ans.first=ans.first*L.first%mod;
		ans.second|=L.second;
	}
	if(r>mid){
		pii R=query(k<<1|1,l,r);
		ans.first=ans.first*R.first%mod;
		ans.second|=R.second;
	}
	return ans;
}
int inv[500];
main(){
	freopen("array.in","r",stdin);
	freopen("array.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	for(int i=1;i<=400;i++)inv[i]=qp(i,mod-2);
	cin>>n>>q;
	for(int i=1;i<=n;i++)cin>>a[i];
	build(1,1,n);
	while(q--){
		cin>>op;
		if(op=="1"){
			cin>>l>>r>>x;
			int p=getp(x);
			update(1,l,r,x,p);
		}
		else{
			cin>>l>>r;
			pii tmp=query(1,l,r);
			int ans=tmp.first,p=tmp.second;
			for(int i=0;i<62;i++){
				if((p>>i)&1){
					ans=ans*(prim[i]-1)%mod*inv[prim[i]]%mod;
				}
			}
			cout<<ans<<"\n";
		}
	}
	return 0;
}

T4 樹 30Pts

原題 ODW

根號分治。
設塊長為 \(T\)
預處理出步長為 \([1,\sqrt T]\) 時每個點向上跳到根的點權和以及每個點的 \(1 \backsim \sqrt T\)
大於 \(T\) 則暴力跳,否則樹上差分。

這題資料很水,暴力有 70,能過除了鏈以外的所有點;加個對鏈的暴力就過了。
但賽時樹剖 LCA 打鍋了導致 \(100 \to 30\)

後記

還是學到了很多的,也確實是技不如人。

之後要注意程式碼細節問題,儘量不要在不該掛的地方掛分。

相關文章