暑假集训CSP提高模拟7

这个 T1 的 \(n^{3}\) 的 SPJ 效率还是太慢了,膜拜 SPJ 大神学长,还会画画

A.Permutations & Primes

这题感觉挺水的但是感觉有不是那么水,主要还是因为我赛时没想出正解,在打的表里找了一组好看的规律,打上了然后就过了. 对偶数来说,我的规律正好是正解的特化,但是对奇数来说,我的规律就很奇怪了,我开头是 \(5\),最中间是 \(1\),结尾是 \(2\) 但是我完全卡不掉,如上图. 感觉是对的,不会证明其正确性.

学长写的 SPJ 挺神的,每次根据答案扩展,我的 \(n^{3}\) 写法比起来就太菜了

#include<bits/stdc++.h>
using namespace std;
int n,a[200001];
int main(){
	int cases;scanf("%d",&cases);while(cases--){
		scanf("%d",&n);
		if(n&1){
			a[(n+1)/2]=1;int tot=1;
			int i1=1,j1=(n+1)/2-1,i2=(n+1)/2+1,j2=n;
			while(i1<=j1 and i2<=j2){
				a[j2]=++tot;j2--;
				a[j1]=++tot;j1--;
				if(i1<=j1 and i2<=j2){
					a[i2]=++tot;i2++;
					a[i1]=++tot;i1++;
				}
			}
			for(int i=1;i<=n;++i){
				printf("%d ",a[i]);
			}
			cout<<endl;
		}
		else{
			int i1=1,j1=n/2,i2=n/2+1,j2=n;int tot=0;
			while(i1<=j1 and i2<=j2){
				a[i2]=++tot;i2++;
				a[i1]=++tot;i1++;
				if(i1<=j1 and i2<=j2){
					a[j2]=++tot;j2--;
					a[j1]=++tot;j1--;
				}
			}
			for(int i=1;i<=n;++i){
				printf("%d ",a[i]);
			}
			cout<<endl;
		}
	}
}

附一个对拍

//create.cpp
#include<bits/stdc++.h>
using namespace std;
namespace rander{
	vector<long long> list;
	int it,ssize;
	int reset(int size){if(size<=0)return -1;int seed=std::rand();mt19937 Rand(seed);ssize=size;it=0;list.clear();for(int i=1;i<=size;++i){list.push_back(Rand());}return seed;}
	int reset(){struct _timeb T;_ftime(&T);srand(T.millitm);return rander::reset(1000);}
	vector<long long> randlist(int size){int seed=ssize;reset(size);vector<long long> ret=list;reset(seed);return ret;}
	int rand(){if(ssize<=0)reset();int ret=list[it];it++;if(it>=ssize)reset(ssize);return ret;}
	int rand(int mod){int ret=rander::rand()%mod;return ret;}
	long long randabs(int mod){long long ret=abs(rander::rand());if(mod)ret%=mod;return ret;}
	long long rand(int l,int r){if(l<0)return rander::rand(0,r);long ret=rander::randabs(r-l+1)+l;return ret;}
	template<typename _T>_T from(vector<_T>p){long long ret=rander::randabs(p.size());return p[ret];}
	template<typename _T>void randsort(vector<_T>&p){for(int i=1;i<=p.size();++i){int x=rander::rand(0,p.size()-1),y=rander::rand(0,p.size()-1);swap(p[x],p[y]);}}
	template<typename _T>void randsort(_T &p,int from,int to){for(int i=1;i<=to-from+1;++i){int x=rander::rand(from,to),y=rander::rand(from,to);swap(p[x],p[y]);}}
};
int main(){
	int n=rander::rand(1,40000)*2+1;
	cout<<"Create [n="<<n<<"]"<<endl;
	freopen("test.in","w",stdout);
	cout<<1<<endl;
	cout<<n<<endl;
}
//checker.cpp
#include<bits/stdc++.h>
using namespace std;
int a[200001];bool isprime[200001];
int main(){
	freopen("test.out","r",stdin);
	
	memset(isprime,true,sizeof isprime);
	isprime[1]=isprime[0]=false;
	for(int i=4;i<=200000;i+=2){
		isprime[i]=false;
	}
	for(int i=3;i<=200000;i+=2){
		if(isprime[i]){
			for(int j=3;j*i<=200000;j+=2){
				isprime[i*j]=false;
			}
		}
	}
	int p=0,ans=0;
	while(cin>>a[p]){
		p++;
	}
	for(int i=1;i<=p-1;++i){
		for(int j=i;j<=p-1;++j){
			int mex=1;
			for(int k=i;k<=j;++k){
				if(a[k]==mex){
					mex++;
					k=i-1;
				}
			}
			if(isprime[mex]) ans++;
		}
	}
	int res=ans;
	cout<<"[Test.check] ok "<<res<<endl;
	freopen("test.check","w",stdout);
	cout<<res<<endl;
}
//maintest.cpp
#include<bits/stdc++.h>
using namespace std;
#include<windows.h>
int main(){
	int cnt=0,wrong=0;
	while(1){
		cnt++;
		cout<<"Test "<<cnt<<" Now Wrong Times:"<<wrong<<endl;
		system("create.exe");int st=clock();
		system("test.exe");int ed=clock();
		system("answer.exe");ed=clock();
		system("checker1.exe");
		system("checker2.exe");ed=clock();
		cout<<"Checking Finished. Cost "<<ed-st<<" ms"<<endl;
		if(system("fc test.check ans.check")){
			wrong++;
			system("pause");
		}
	}
}

B.树上游戏

真有 \(50w\) 个吗,能不能送我一个

没想到答案居然有单调性,但是答案确实有单调性. 如果能选 \(k\) 种颜色就一定能选 \(\lt k\) 种颜色,因此可以用二分答案做.

现在问题就是 check() 怎么写,发现我们可以只考虑深度最大的节点往回搜,当搜到 \(mid\) 距离的时候就说明至少要在这里放一个点,否则就走不到了,因此我们据此来统计节点个数,然后跟 \(k\) 比较作为二分依据

#include<bits/stdc++.h>
using namespace std;
int n,k,ans;
int a[200001],b[200001];
int num,len;
vector<int>e[200001];
void dfs(int now,int last){
	int p=-1,q=0;
	for(int i:e[now]){
		if(i!=last){
			dfs(i,now);
			p=max(p,a[i]-1);
			q=max(q,b[i]+1);
		}
	}
	if(p>=q){
		a[now]=p;
		b[now]=-1;
		return;
	}
	if(q<len){
		a[now]=0;
		b[now]=q;
		return;
	}
	num++;
	a[now]=len;
	b[now]=-1;
}
bool check(int x){
	memset(a,-1,sizeof a);
	memset(b,-1,sizeof b);
	len=x,num=0;
	dfs(1,0);
	if(b[1]!=-1){
		num++;
	}
	if(num>k) return false;
	return true;
}
int main(){
	cin>>n>>k;
	for(int i=1;i<=n-1;++i){
		int x,y;cin>>x>>y;
		e[x].push_back(y);
		e[y].push_back(x);
	}
	int l=1,r=200000;
	while(l<=r){
		int mid=(l+r)/2;
		if(check(mid)){
			r=mid-1;
			ans=mid;
		}
		else{
			l=mid+1;
		}
	}
	cout<<ans<<endl;
}

C.Ball Collector

考虑对冲突的部分连边(还是记一下吧,挺套路的,上一个用这个套路的题还是二分图最大独立集)

连完以为是道不可做,结果能手玩一下出性质,性质就是树只能选出 \(x-1\) 个种类不同的数,其余情况均能选出 \(x\)

我在考场上大抵是没这么大胆猜这种结论.

维护是不是树,那么可以通过维护一个并查集,然后看它的 \(size\) 来决定,但是还需要写撤销操作,所以要用可撤销并查集,可撤销并查集思路还是挺巧的,用了按秩合并来维护平衡

#include<bits/stdc++.h>
using namespace std;
const int p=998244353;
int n;
int power(int x,int y){
	int ans=1,base=x;
	while(y){
		if(y&1){
			ans=1ll*ans*base%p;
		}
		y>>=1;
		base=1ll*base*base%p;
	}
}
int inv(int x){
	return power(x,p-2)%p;
}
pair<int,int> stk[200001];int top;
int num[200001];
bool tag[200001];
int fa[200001],size[200001],ans;
int find(int x){
	return x==fa[x]?x:find(fa[x]);
}
void join(int a,int b){
	int x=find(a),y=find(b);top++;
	if(x==y){
		if(num[x]==size[x]-1){
			ans++;
			tag[top]=true;
		}
		num[x]++;
		stk[top]={-1,x};
	}
	else{
		if(size[x]>size[y]) swap(x,y);
		if(num[x]==size[x]-1 or num[y]==size[y]-1){
			ans++;
			tag[top]=true;
		}
		size[y]+=size[x];
		fa[x]=y;
		stk[top]={x,y};
		num[y]+=num[x]+1;
	}
}
void undo(int id){
	if(stk[id].first==-1) num[stk[id].second]--;
	else{
		int x=stk[id].first,y=stk[id].second;
		fa[x]=x;
		size[y]-=size[x];
		num[y]-=num[x]+1;
	}
	if(tag[id]) ans--;
	tag[id]=false;
	stk[id]={0,0};
}
int _ans[200001],a[200001],b[200001];
vector<int>e[200001];
void dfs(int now,int fa){
	int cur=top;
	join(a[now],b[now]);
	_ans[now]=ans;
	for(int i:e[now]){
		if(i!=fa){
			dfs(i,now);
		}
	}
	while(top>cur) undo(top--);
}
int main(){
	cin>>n;
	for(int i=1;i<=n;++i){
		cin>>a[i]>>b[i];
	}
	for(int i=2;i<=n;++i){
		int x,y;cin>>x>>y;
		e[x].push_back(y);
		e[y].push_back(x);
	}
	for(int i=1;i<=n;++i){
		size[i]=1,fa[i]=i;
	}
	dfs(1,0);
	for(int i=2;i<=n;++i){
		cout<<_ans[i]<<" ";
	}
} 

D.满穗

posted @ 2024-07-25 21:42  HaneDaniko  阅读(22)  评论(2编辑  收藏  举报