CSP提高组模拟1

我的微軟輸入法莫名其妙變成繁體了,你們有什麽頭緒嗎

狀態 題目
20 Time Exceeded A 最短路
25 Time Exceeded B 方格取数
0 Time Exceeded C 数组
70 Time Exceeded D 树

A.最短路

我赛时想了想,会不会 DIJ 不是很对,因为这个题在打的时候觉得,在跑最短路的时候沿途加上点权有点草率了,但是后面没写完,所以也没回来想

有点像

我四十分的物理卷子

(内心):你做的这个对吗

我:管它呢,先画个圈,一会回来想,要不写不完了

(内心):你本来也写不完,要不再看看这题

(在想起一些不好的回忆(因为太磨叽挂了五十分)之后,还是毅然地跳了)

(过了一会)

(内心):跳了这么多,你这不也没写完

我:我草我前面还有题没想

所以这个题还是用 Floyed,为啥要点权从小到大排序,其实这么一听觉得真是太对了,但是我懒得想了,所以还是三遍 Floyed 来得实在(只需要保证完全更新就行了)

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int inf=0x3f3f3f3f3f3f3f3f;
int mp[301][301];
int dis[301][301];
int w[301];
int n,m,q;
void floyd(){
    for(int k=1;k<=n;k++){
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                int p=mp[i][k]+mp[k][j]+max(dis[i][k],dis[k][j]);
                if((p<mp[i][j]+dis[i][j])&&mp[i][k]<inf&&mp[k][j]<inf){
                    mp[i][j]=mp[i][k]+mp[k][j];
                    dis[i][j]=max(dis[i][k],dis[k][j]);
                }
            }
        }
    }
}
signed main(){
	freopen("path.in","r",stdin);
	freopen("path.out","w",stdout);
    cin>>n>>m>>q;
    for(int i=1;i<=n;i++){
        cin>>w[i];
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(i==j) mp[i][j]=0;
            else mp[i][j]=inf;
        }
    }
    for(int i=1;i<=m;i++){
        int x,y,z;
        cin>>x>>y>>z;
        if(mp[x][y]>z){
            mp[x][y]=z;
            mp[y][x]=z;
            dis[x][y]=max(w[x],w[y]);
            dis[y][x]=max(w[x],w[y]);
        }
    }
    floyd();floyd();floyd();
    for(int i=1;i<=q;i++){
        int x,y;
        cin>>x>>y;
        int res=mp[x][y]+dis[x][y];
        cout<<(res>=0x3f3f3f3f3f3f3f3f?-1:res)<<endl;
    }
}

B. 方格取数

这个题的 \(n^4\) 挺好想的,我因为没什么头绪打的也是 \(n^4\) 暴力,没想到还能拿点分

如果有一个点大于等于 \(k\) 且小于等于 \(2k\) ,那么我们直接输出这个点即可

如果有一个点大于 \(2k\) ,那么我们一定不可以选这个点,和包括它的矩形

那么现在这个矩阵上只剩下不能选的点和权值小于 \(k\) 的点了,否则跑不到这里就输出答案了.

因此我们现在在这个简化的矩阵上考虑答案

我们可以考虑先找出一个最大的矩阵,即使它的权值大于 \(2k\),在我们一点点减少的过程中,因为所有节点都小于 \(k\),一定会在 \([k,2k]\) 内出现一次,因此直接大力删就行了. 删下来以后(其实就是切开了)要判一下哪边比较大(或者直接合法了,那更好)

洛谷上过了,咱们题库上 \(n\)\(k\) 居然是反的,答案坐标也是反的 ,题库上莫名其妙 T 了, \(40\) 分,懒得改了,这并不是 AC 代码

#include<bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
#define N 2001
int k,n;
long long a[N][N];
long long sum[N][N];
int up[N][N],leftt[N],rightt[N];
long long ask(int lx,int ly,int ux,int uy){
	return sum[ux][uy]-sum[lx-1][uy]-sum[ux][ly-1]+sum[lx-1][ly-1];
}
long long sumx;
int mat_sx,mat_sy,mat_ex,mat_ey;
void cutline(int x,int l,int r){
	sumx=ask(x,l,x,r);
	for(int i=r;i>=l;--i){
		sumx-=a[x][i];
		if(sumx>=k and sumx<=(k<<1)){
			printf("%d %d %d %d",x,l,x,i-1);
			exit(0);
		}
	}
}
void update(int lx,int ly,int ux,int uy){
	long long t=ask(lx,ly,ux,uy);
	if(t>=k and t<=(k<<1)){
		printf("%d %d %d %d",lx,ly,ux,uy);
		exit(0);
	}
	if(t>sumx){
		sumx=t;
		mat_sx=lx,mat_sy=ly,mat_ex=ux,mat_ey=uy;
	}
}
stack<int>st;
signed main(){
	freopen("matrix.in","r",stdin);
	freopen("matrix.out","w",stdout);
	cin>>n>>k;
	for(int i=1;i<=n;++i){
		for(int j=1;j<=n;++j){
			cin>>a[i][j];
			sum[i][j]=sum[i][j-1]+a[i][j];
			if(a[i][j]>=k and a[i][j]<=(k<<1)){
				printf("%d %d %d %d",i,j,i,j);
				return 0;
			}
		}
		for(int j=1;j<=n;++j){
			sum[i][j]+=sum[i-1][j];
		}
	}
	for(int i=1;i<=n;++i){
		for(int j=1;j<=n;++j){
			if(a[i][j]<k){
				up[i][j]=up[i-1][j]+1;
			}
		}
		while(!st.empty()) st.pop();
		st.push(0);
		up[i][0]=-1;
		for(int j=1;j<=n;++j){
			while(!st.empty() and up[i][st.top()]>=up[i][j]) st.pop();
			leftt[j]=st.top()+1;
			st.push(j);
		}
		while(!st.empty()) st.pop();
		st.push(n+1);
		up[i][n+1]=-1;
		for(int j=n;j>=1;--j){
			while(!st.empty() and up[i][st.top()]>=up[i][j]) st.pop();
			rightt[j]=st.top()-1;
			st.push(j);
			if(up[i][j]) update(i-up[i][j]+1,leftt[j],i,rightt[j]);
		}
	}
	if(sumx<k){
		printf("-1\n");
		return 0;
	}
	for(int i=mat_ex;i>=mat_sx;--i){
		long long t=ask(i,mat_sy,i,mat_ey);
		if(t>=k and t<=(k<<1)){
			printf("%d %d %d %d",i,mat_sy,i,mat_ey);
			return 0;
		}
		else if(t>(k<<1)){
			cutline(i,mat_sy,mat_ey);
		}
		else{
			sumx-=t;
			if(sumx>=k and sumx<=(k<<1)){
				printf("%d %d %d %d",mat_sx,mat_sy,i-1,mat_ey);
				return 0;
			}
		}
	}
}

C.数组

没想到搞了半天,线段树写对了,状压写对了,\(tag\) 写对了(其实是没写对,但是差不多对了),但是求 \(\phi\) 的时候寄了

🤣👉👉👉👉👉👉👉👉👉👉👉👉

这个题就是显然了,欧拉函数只要会求的就明白,直接根据积性函数(而且质因数也是差不多那种性质,所以极其好维护,取个或就行了)维护一下区间欧拉函数,扔到线段树里,拿出来的时候做个乘法就行了.

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int p=1000000007;
int power(int x,int t,int mod){
	int ans=1,base=x;
	while(t){
		if(t&1){
			ans=(ans*base)%mod;
		}
		base=base*base%mod;
		t>>=1;
	}
	return ans;
}
struct tree{
	int val;
	int p;
	int lazy,lazyP;
	tree(){
		val=1;
		lazy=1;
		p=0;
		lazyP=0;
	}
} t[1600001];
int n,q;
int a[1600001];
int f[1001],inv[1001];
int prime[]={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};
void update(int x){
	t[x].val=t[x*2].val*t[x*2+1].val%p;
	t[x].p=t[x*2].p|t[x*2+1].p;
}
void pushdown(int x,int l,int m,int r){
    t[x*2].val=t[x*2].val*power(t[x].lazy,m-l+1,p)%p;
    t[x*2+1].val=t[x*2+1].val*power(t[x].lazy,r-m,p)%p;
    t[x*2].lazy=t[x*2].lazy*t[x].lazy%p;
    t[x*2+1].lazy=t[x*2+1].lazy*t[x].lazy%p;
    t[x*2].p|=t[x].lazyP;
    t[x*2+1].p|=t[x].lazyP;
    t[x*2].lazyP|=t[x].lazyP;
    t[x*2+1].lazyP|=t[x].lazyP;
	t[x].lazy=1;
	t[x].lazyP=0;
}
void build(int l,int r,int x){
	if(l==r){
		t[x].val=a[l];
		for(int i=0;i<62;++i){
			if(a[l]%prime[i]==0){
				t[x].p|=(1ll)<<i;
			}
		}
		return;
	}
	int m=(l+r)/2;
	build(l,m,x*2);
	build(m+1,r,x*2+1);
	update(x);
}
void update(int x,int l,int r,int delta,int prime,int cl,int cr){
	if(cl>r or cr<l) return;
	if(l<=cl and cr<=r){
		t[x].val=t[x].val*power(delta,cr-cl+1,p)%p;
		t[x].lazy=t[x].lazy*delta%p;
		t[x].p|=prime;
		t[x].lazyP|=prime;
		return;
	}
	const int m=(cl+cr)/2;
	pushdown(x,cl,m,cr);
	update(x*2,l,r,delta,prime,cl,m);
	update(x*2+1,l,r,delta,prime,m+1,cr);
	update(x);
}
tree find(int x, int l, int r, int cl, int cr) {
	if(cl>r or cr<l) return tree();
	if(l<=cl and r>=cr) return t[x];
	int m=(cl+cr)/2;
	pushdown(x,cl,m,cr);
	tree n;
	tree ll=find(x*2,l,r,cl,m);
	tree rr=find(x*2+1,l,r,m+1,cr);
	n.val=ll.val*rr.val%p;
	n.p=ll.p|rr.p;
	return n;
}
signed main(){
	freopen("array.in","r",stdin);
	freopen("array.out","w",stdout);
	cin>>n>>q;
	for(int i=1;i<=n;++i){
		cin>>a[i];
	}
	build(1,n,1);
	inv[1]=1;
	for(int i=2;i<=300;++i){
		inv[i]=p-(p/i)*inv[p%i]%p;
	}
	for(int i=0;i<62;++i){
		f[i]=inv[prime[i]]*(prime[i]-1)%p;
	}
	for(int i=1;i<=q;i++){
		int opt,x,y;
		cin>>opt>>x>>y;
		if(opt==1){
			int k;
			cin>>k;
			int t=0;
			for(int i=0;i<62;++i){
				if(k%prime[i]==0){
					t|=(1ll)<<i;
				}
			}
			update(1,x,y,k,t,1,n);
		}
		else{
			tree ans=find(1,x,y,1,n);
			int phi=ans.val;
			for(int i=0;i<62;++i){
				if(ans.p>>i&1){
					phi=phi*f[i]%p;
				}
			}
			cout<<phi%p<<endl;
		}
	}
}

D.

懒得喷,谁造了组链的数据,还给那么大分

随机数据的话,考虑到先求个 Lca,然后两个节点都往 Lca 跳,每次都跳步长,一直到跳不动了,我们可以发现此时无论两个点位于哪里,中间一定不会再有其他点了.

因此倍增优化下 Lca,正好也能用来二进制优化跳步长,就能拿到这道题的大部分分.

啥比链,不写,感谢 luobotianle 提供的只过了链的假代码,拼上就行了

UPD: 原來链的不用根号分治也能过,这数据也太水了

#include<bits/stdc++.h>
using namespace std;
int n,w[50001];
vector<int>e[50001];
int fa[17][50001],deep[50001];
int xx[50001],yy[50001];
void dfs(int now,int last,int nowdeep){
	fa[0][now]=last;
	deep[now]=nowdeep;
	for(int i:e[now]){
		if(i!=last) dfs(i,now,nowdeep+1);
	}
}
void prework(){
	for(int i=1;i<=16;++i){
		for(int j=1;j<=n;++j){
			fa[i][j]=fa[i-1][fa[i-1][j]];
		}
	}
}
int jumpto(int x,int step){
	int st=0;
	while(step){
		if(step&1){
			x=fa[st][x];
		}
		step>>=1;
		st++;
	}
	return x;
}
int lca(int x,int y){
	if(deep[x]<deep[y]) swap(x,y);
	for(int i=16;i>=0;--i){
//		cout<<"deep "<<deep[x]<<" "<<deep[y]<<" "<<(1<<i)<<endl;
		if(deep[x]-deep[y]>=(1<<i)){
//			cout<<"jump "<<i<<endl;
			x=fa[i][x];
		}
	}
	for(int i=16;i>=0;--i){
		if(fa[i][x]!=fa[i][y]){
			x=fa[i][x];
			y=fa[i][y];
		}
	}
//	cout<<deep[x]<<" = "<<deep[y]<<endl;
//	while(deep[x]!=deep[y]) x=fa[0][x];
//	int cnt=0;
//	while(x!=y){
//		cnt++;
//		x=fa[0][x],y=fa[0][y];
//	}
//	cout<<"jumpcnt "<<cnt<<endl;
//	return x;
	return x==y?x:fa[0][x];
}
int not_lca(int x,int y,int steplen){
	int ans=w[x]+w[y],root=lca(x,y);
//	cout<<"root "<<root<<endl;
//	cout<<"++ "<<x<<" "<<y<<endl;
	while(deep[x]-deep[root]>=steplen){
		x=jumpto(x,steplen);
		ans+=w[x];
//		cout<<"+ "<<x<<endl;
	}
	while(deep[y]-deep[root]>=steplen){
		y=jumpto(y,steplen);
//		if(y!=root){
			ans+=w[y];
//			cout<<"+ "<<y<<endl;
//		}
	}
	if(x==y) ans-=w[y];
	return ans;
}
namespace lian{
	const int N=5e4+5;
	int a[N],b[N],c[N],x,y;
	struct edge{
		int next,to;
	}e[N*2];
	int h[N],cnt;
	void add(int u,int v){
		e[++cnt]={h[u],v};
		h[u]=cnt;
	}
	int fa[N],top[N],son[N],dep[N],siz[N];
	void dfs1(int x,int f){
		fa[x]=f;
		dep[x]=dep[f]+1;
		siz[x]=1;
		int maxc=0;
		for(int i=h[x];i;i=e[i].next){
			int to=e[i].to;
			if(to==f)continue;
			dfs1(to,x);
			siz[x]+=siz[to];
			if(siz[to]>maxc){
				maxc=siz[to];
				son[x]=to;
			}
		}
	}
	void dfs2(int x,int topf){
		top[x]=topf;
		if(!son[x])return;
		dfs2(son[x],topf);
		for(int i=h[x];i;i=e[i].next){
			int to=e[i].to;
			if(to==fa[x]||to==son[x])continue;
			dfs2(to,to);
		}
	}
	int lca(int x,int y){
		while(top[x]!=top[y]){
	//		cout<<dep[top[x]]<<" "<<dep[top[y]]<<"\n";
			if(dep[top[x]]<dep[top[y]])swap(x,y);
			x=fa[top[x]];
		}
		return x;
	}
	void solve1(){
		dfs2(1,1);dfs2(1,1);
		for(int i=1;i<=n;i++)cin>>b[i];
		for(int i=2;i<=n;i++)cin>>c[i];
		for(int i=2;i<=n;i++){
			int st=b[i-1],ed=b[i];
			if(st>ed)swap(st,ed);
			int ans=0;
			for(int j=st;j<=ed;j+=c[i]){
				ans+=a[j];
			}
			cout<<ans<<"\n";
		}
	}
	int main(){
		for(int i=1;i<=n;++i){
			a[i]=w[i];
		}
		for(int i=1;i<n;i++){
			add(xx[i],yy[i]);
			add(yy[i],xx[i]);
		}
		solve1();
		return 0;
		dfs1(1,1);dfs2(1,1);
	//	for(int i=1;i<=n;i++){
	//		cout<<fa[i]<<" "<<dep[i]<<"\n";
	//	}
		for(int i=1;i<=n;i++)cin>>b[i];
		for(int i=2;i<=n;i++)cin>>c[i];
		for(int i=2;i<=n;i++){
			int st=b[i-1],ed=b[i];
			int t=c[i];
			int f=lca(st,ed);
			int ans=0;
			if(f==st||f==ed){
				if(dep[st]<dep[ed])swap(st,ed);
				ans=a[st];
				while(st!=ed){
					for(int j=1;j<=t;j++)st=fa[st];
					ans+=a[st];
				}
				cout<<ans<<"\n";
			}
			else{
				ans=a[st]+a[ed];
				while(dep[st]-dep[f]>=t){
					for(int j=1;j<=t;j++)st=fa[st];
					ans+=a[st];
				}
				while(dep[ed]-dep[f]>=t){
					for(int j=1;j<=t;j++)ed=fa[ed];
					ans+=a[ed];
				}
				if(st==f&&ed==f)ans-=a[f];
				cout<<ans<<"\n";
			}
		}
		return 0;
	}
}
int b[50001],c[50001];
int main(){
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
	cin>>n;
	for(int i=1;i<=n;++i){
		cin>>w[i];
	}
	bool islian=true;
	for(int i=1;i<=n-1;++i){
		cin>>xx[i]>>yy[i];
		if(abs(xx[i]-yy[i])!=1) islian=false;
		e[xx[i]].push_back(yy[i]);
		e[yy[i]].push_back(xx[i]);
	}
	if(islian){
		lian::main();
	}
	else{
		dfs(1,0,1);
		prework();
		for(int i=1;i<=n;++i){
			cin>>b[i];
		}
		for(int i=1;i<=n-1;++i){
			cin>>c[i];
		}
		for(int i=1;i<=n-1;++i){
			cout<<not_lca(b[i],b[i+1],c[i])<<endl;
		}
	}
}
posted @ 2024-07-12 16:13  HaneDaniko  阅读(40)  评论(1编辑  收藏  举报