ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

CSP集训题解

2022-09-09 21:00:45  阅读:184  来源: 互联网

标签:typedef int 题解 Rep long 集训 maxn CSP define


CSP集训题解

Summer已经完结了于是新开一个,而且旧的已经很卡了

9.9CSP-S短赛2(开小灶2)

T1 元素周期表

看来是一种套路,将每个点对应的x,y坐标连边,在一个联通块内的就是可达的,那么答案就是联通块数减一

点击查看代码


#include <bits/stdc++.h>
typedef long long ll;typedef unsigned long long ull; typedef double db;typedef long double ldb;
#define fre(x) freopen(#x ".in","r",stdin),freopen(#x ".out","w",stdout)
#define Rep(i,a,b) for(int i=a;i<=b;++i) 
#define Dwn(i,a,b) for(int i=a;i>=b;--i)
#define pii pair<int,int>
#define mair make_pair
#define fir first
#define sec second
using namespace std;

const int maxn=4e5+10;

struct eg{int from,to,next;}e[maxn*2];
int len,head[maxn];bool vis[maxn];
int ans,n,m,q;
void lqx(int from,int to)
{ e[++len].from=from,e[len].to=to,e[len].next=head[from],head[from]=len; }
void Dfs(int u){
	vis[u]=true;
	for(int i=head[u];i;i=e[i].next)if(!vis[e[i].to])Dfs(e[i].to);
}

void solve(){
	cin>>n>>m>>q;int x,y;
	Rep(i,1,q)cin>>x>>y,lqx(x,n+y),lqx(n+y,x);
	Rep(i,1,n+m)if(!vis[i])++ans,Dfs(i);
	cout<<ans-1;
}

int main (){ ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);return solve(),0; }

T2 gcd

很是裸。用莫比乌斯函数换gcd,化出来就是

\[ \sum_{d |A} \sum_{i=1}^{n} \mu(d)*[d|a_i]* vis[i] \]

显然每个数贡献的位置只会有\(\log n\)个,统计贡献的位置同样,于是预处理下约数,维护一下每个\(d\)的贡献就完了。

点击查看代码


#include <bits/stdc++.h>
typedef long long ll;typedef unsigned long long ull; typedef double db;typedef long double ldb;
#define fre(x) freopen(#x ".in","r",stdin),freopen(#x ".out","w",stdout)
#define Rep(i,a,b) for(int i=a;i<=b;++i) 
#define Dwn(i,a,b) for(int i=a;i>=b;--i)
#define pii pair<int,int>
#define mair make_pair
#define fir first
#define sec second
using namespace std;

const int maxn=2e5+10,NN=5e5;

int Mu[NN+10],cnt,prime[maxn];
ll f[NN+10];
bool vis[NN+10];
int n,m;
ll ans=0,num=0;
bool in[maxn];
int a[maxn];
vector<int>vec[NN+10];

void Get(const int N){
	Mu[1]=1;
	for(int i=2;i<=N;i++){
		if(!vis[i])prime[++cnt]=i,Mu[i]=-1;
		for(int j=1;j<=cnt&&i*prime[j]<=N;j++){
			vis[i*prime[j]]=true;
			if(i%prime[j]==0){Mu[i*prime[j]]=0;break;}
			Mu[i*prime[j]]=-Mu[i];
		}
	}
	Rep(i,2,N){
		vec[i].push_back(1);
		for(int j=1;i*j<=N;++j)vec[i*j].push_back(i);
	}
}

void solve(){
	Get(NN);
	cin>>n>>m;
	Rep(i,1,n)cin>>a[i];
/*	while(m--){
		int x;cin>>x;int res=0;
		Rep(i,1,n)if(in[i] && i!=x){
			for(int j=1;j<=5;++j){
				if((a[i]%j==0) && (a[x]%j==0))res+=Mu[j];
			}
		}
		if(!in[x])ans+=res;
		else ans-=res;
		in[x]^=1;
		cout<<ans<<"\n";
	}
*/
	while(m--){
		int x;cin>>x;
		ll res=0;
		if(a[x]==1){
			if(in[x]){
				--num;ans-=num;
				f[1]-=Mu[1];
			}else{
				ans+=num;++num;
				f[1]+=Mu[1];
			}
			in[x]^=1;
			cout<<ans<<"\n";
			continue;
		}
		if(in[x]){
			for(auto it : vec[a[x]]){
				f[it]-=Mu[it];
				res+=f[it];
			}
			ans-=res;in[x]^=1;--num;
		}else{
			for(auto it : vec[a[x]]){
				res+=f[it];
				f[it]+=Mu[it];
			}
			ans+=res;in[x]^=1;++num;
		}
		cout<<ans<<"\n";
	}
}

int main (){ ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);return solve(),0; }

9.5CSP-S短赛1(开小灶1)

T1ZZH的游戏

实际上把策略想明白就很简单。以一次连续的移动为一个阶段,每个阶段都必定都扩展到一个更小的点,类似贪心。

我们可以每次贪心的把能走的小点都走了,于是我们现在就被一圈大点圈起来了,这时候必定有一个人需要走一个大点来打破僵局,因为我们需要到达1。于是我们贪心的选一个所有备选点的最小的点走(同时另一个人移动到它已经扩展过的最小的点),然后同样的贪心把能走的小点都走了,再考虑大点即可。这样保证每次都是答案尽量小的增长,两边都扩展到了1就停止。所以比较裸的就是直接拿一个优先队列维护所有备选点,这样做是\(O(n \log n)\)的,但是由于那只log是STL造成的,于是开了O2就有无限可能(实际上比线性快)。线性做法就是把优先队列换成桶,从小往大扫就行了。

点击查看代码


#include <bits/stdc++.h>
typedef long long ll;typedef unsigned long long ull; typedef double db;typedef long double ldb;
#define fre(x) freopen(#x ".in","r",stdin),freopen(#x ".out","w",stdout)
#define Rep(i,a,b) for(int i=a;i<=b;++i) 
#define Dwn(i,a,b) for(int i=a;i>=b;--i)
#define pii pair<int,int>
#define mair make_pair
#define fir first
#define sec second
using namespace std;

const int maxn=1e6+10;

int Lim,ans;
int n;
int tex;
int Gs,Fs;

struct Graph{
	struct eg{int from,to,next;}e[maxn*2];
	int len,head[maxn];bool vis[maxn];
	priority_queue< int,vector<int>,greater<int> >q;
	void lqx(int from,int to)
	{ e[++len].from=from;e[len].to=to,e[len].next=head[from],head[from]=len; }
	void Clear(){Rep(i,1,n)head[i]=0,vis[i]=0;len=0;priority_queue< int,vector<int>,greater<int> >temp;q=temp;}
	void Extend(int u){
		vis[u]=1;
		for(int i=head[u];i;i=e[i].next){
			int v=e[i].to;if(vis[v])continue;
			q.push(v);
		}
	}
}G,F;

void Sol(){
	while(!G.q.empty() || !F.q.empty()){
		if(G.vis[1] && F.vis[1])break;
		if(G.q.empty()){
			int u=F.q.top();F.q.pop();
			ans=max(ans,Gs+u);Fs=min(Fs,u);
			F.Extend(u);
			continue;
		}
		if(F.q.empty()){
			int u=G.q.top();G.q.pop();
			ans=max(ans,Fs+u);Gs=min(Gs,u);
			G.Extend(u);
			continue;
		}
		int u=G.q.top(),v=F.q.top();
		if(Gs+v<Fs+u){
			F.q.pop();
			ans=max(ans,Gs+v);Fs=min(Fs,v);
			F.Extend(v);
		}
		else{
			G.q.pop();
			ans=max(ans,Fs+u);Gs=min(Gs,u);
			G.Extend(u);
		}
	}
	cout<<ans<<"\n";
}

void solve(){
	cin>>n;G.Clear(),F.Clear();int x,y;
	Rep(i,2,n)cin>>x>>y,G.lqx(x,y),G.lqx(y,x);
	Rep(i,2,n)cin>>x>>y,F.lqx(x,y),F.lqx(y,x);
	cin>>Gs>>Fs;ans=Gs+Fs;
	G.Extend(Gs),F.Extend(Fs);
	Sol();
}

int main (){ ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);cin>>tex;while(tex--)solve(); }


T2ZZH的背包

稍劣一点的是5e8的双指针,考虑折半把两边的所有方案都预处理出来然后合并,就是枚举其中一边的选法,然后在另一边二分查找合法区间,然后由于两边都是有序的,所以左右边界都是单调不升的,双指针即可。

正解是1e7的神奇做法,首先预处理出一边的方案,用归并达到\(O(n)\),然后枚举另一边的每个物品选不选。把询问离线下来拆成两个小于等于,然后在一边选就相当于在另一边的询问变小了,把所有在另一边的询问跑出来,然后单指针扫一遍统计贡献就行了。由于做法诡异需要卡空间。

点击查看代码


#include <bits/stdc++.h>
typedef long long ll;typedef unsigned long long ull; typedef double db;typedef long double ldb;
#define fre(x) freopen(#x ".in","r",stdin),freopen(#x ".out","w",stdout)
#define Rep(i,a,b) for(int i=a;i<=b;++i) 
#define Dwn(i,a,b) for(int i=a;i>=b;--i)
#define pii pair<int,int>
#define mair make_pair
#define fir first
#define sec second
using namespace std;

const int maxn=1e2+10;

int n,q;
ll v[maxn];
ll a[1<<20|1],b[1<<20|1];
int na,nb,Va,Vb;

void solve(){
	cin>>n>>q;Rep(i,1,n)cin>>v[i];
	na=n>>1,nb=n-na;
	Va=1<<na,Vb=1<<nb;
	for(int i=0;i<Va;++i) for(int j=0;j<na;++j)a[i+1]+=((i>>j)&1)*v[j+1];
	for(int i=0;i<Vb;++i) for(int j=0;j<nb;++j)b[i+1]+=((i>>j)&1)*v[na+j+1];
	sort(a+1,a+Va+1),sort(b+1,b+Vb+1);
	while(q--){
		ll l,r;cin>>l>>r;ll ans=0;
		int pl=lower_bound(b+1,b+Vb+1,l)-b,pr=upper_bound(b+1,b+Vb+1,r)-b-1;
		if(pr>=pl)ans+=pr-pl+1;
		Rep(i,2,Va){
	//		while(pl<=n && a[i]+b[pl]<l)++pl;
			while(pl>1 && a[i]+b[pl-1]>=l)pl--;
	//		while(pr<n && a[i]+b[pr]<r)++pr;
			while(pr>=1 && a[i]+b[pr]>r)pr--;
			if(pr>=pl)ans+=pr-pl+1;
		}
		cout<<ans<<"\n";
	}
}

int main (){ ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);return solve(),0; }

9.4CSP-S模拟2

T1谜之阶乘

下降幂的长度最长是二十,于是可以枚举长度,二分下降幂的起点找。

另一种是长度为d的下降幂一定在n开d次根的附近浮动,于是直接暴力\(d^2\)判一遍就行,注意pow的精度问题

点击查看代码


#include <bits/stdc++.h>
typedef long long ll;typedef unsigned long long ull; typedef double db;typedef long double ldb;
#define fre(x) freopen(#x ".in","r",stdin),freopen(#x ".out","w",stdout)
#define Rep(i,a,b) for(int i=a;i<=b;++i) 
#define Dwn(i,a,b) for(int i=a;i>=b;--i)
#define pii pair<int,int>
#define mair make_pair
#define fir first
#define sec second
#define int ll
using namespace std;

const int maxn=1e18;

int tex;
int n;

vector<pii>ans;

void Check(int n,int x){
	int lim=pow(n,1.0/x);
	for(int i=max(2LL,lim-x);i<=lim;++i){
		int now=1;
		for(int j=i;j<=lim+x;++j){
			now*=j;if(now>n || now<0)break;
			if(now==n){ans.emplace_back(j,i-1);break;}
		}
	}
}

void solve(){
	cin>>n;if(n==1)return cout<<"-1\n",void();
	ans.clear();
	Dwn(i,14,1)Check(n,i);
	ans.emplace_back(n,n-1);
	sort(ans.begin(),ans.end());
	ans.erase(unique(ans.begin(),ans.end()),ans.end());
	cout<<ans.size()<<"\n";
	for(auto it : ans)cout<<it.fir<<" "<<it.sec<<"\n";
}

#undef int
int main (){ ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);cin>>tex;while(tex--)solve(); }

T2子集

构造题,在子集siz为偶数时有较好的性质,于是考虑把奇数情况变成偶数即可,单独处理前三个k,将前两个k配对构造成公差为1的等差数列,与第三列倒序匹配,那么前三列就解决了,于是问题退化成偶数情况。判一下非法就行了

点击查看代码


#include <bits/stdc++.h>
typedef long long ll;typedef unsigned long long ull; typedef double db;typedef long double ldb;
#define fre(x) freopen(#x ".in","r",stdin),freopen(#x ".out","w",stdout)
#define Rep(i,a,b) for(int i=a;i<=b;++i) 
#define Dwn(i,a,b) for(int i=a;i>=b;--i)
#define pii pair<int,int>
#define mair make_pair
#define fir first
#define sec second
using namespace std;

const int maxn=1e6+10;

int tex,n,K,siz;

void Work1(){
	cout<<"Yes\n";
	int now=0;int lim=n>>1;
	Rep(i,1,lim){
		cout<<i<<" "<<n-i+1<<" ";
		now+=2;
		if(now==siz){now=0;cout<<"\n";}
	}
}

bool cmp(pii a,pii b){return a.fir+a.sec>b.fir+b.sec;}

pii a[maxn];

void Work2(){
	cout<<"Yes\n";
	Rep(i,1,K)a[i].fir=(i+(K>>1)-1)%K+1,a[i].sec=i+K;
	sort(a+1,a+K+1,cmp);
	int pl=3*K+1,pr=n;
	Rep(i,1,K){
		cout<<a[i].fir<<" "<<a[i].sec<<" "<<2*K+i<<" ";
		int now=3;
		while(now<siz){
			cout<<pl<<" "<<pr<<" ";++pl,--pr;
			now+=2;
		}
		cout<<"\n";
	}
}

void solve(){
	cin>>n>>K;
	siz=n/K;
	if(n==1)return cout<<"Yes\n1\n",void();
	if(siz==1)return cout<<"No\n",void();
	if(siz%2==0)return Work1();
	if(!(K&1))cout<<"No\n",void();
	else return Work2();
}

int main (){ ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);cin>>tex;while(tex--)solve(); }

T3混凝土粉末

发现每次修改操作的编号与高度都是单增的,于是考虑二分答案,找到这个位置上第一次超过阈值的时刻。常数较大且空间复杂度错误的做法是主席树,可以擦边拿到89分或95分(有一个点看常数和运气)。正解就是把主席树换掉,把操作离线下来,像扫描线一样差分,然后只在一颗树上干就行了,树状数组上二分不太会,但是6秒显然是想把两只log放过去。写线段树就一只log了

点击查看代码


#include <bits/stdc++.h>
typedef long long ll;typedef unsigned long long ull; typedef double db;typedef long double ldb;
#define fre(x) freopen(#x ".in","r",stdin),freopen(#x ".out","w",stdout)
#define Rep(i,a,b) for(int i=a;i<=b;++i) 
#define Dwn(i,a,b) for(int i=a;i>=b;--i)
#define pii pair<int,int>
#define mair make_pair
#define fir first
#define sec second
#define int ll
using namespace std;

const int maxn=1e6+10;

#define gc if(++ip==ie)fread(ip=buf,1,SZ,stdin)
static const int SZ=1<<22;char buf[SZ],*ie=buf+SZ,*ip=ie-1;
inline int read(){ gc;while(*ip<'-')gc; bool f=*ip=='-';if(f)gc; int x=*ip&15;gc; while(*ip>'-'){x*=10;x+=*ip&15;gc;} return f ? -x : x; }
inline ll readL(){ gc;while(*ip<'-')gc; bool f=*ip=='-';if(f)gc; ll x=*ip&15;gc; while(*ip>'-'){x*=10;x+=*ip&15;gc;} return f ? -x : x; }

int n,q;
int chk[maxn];
bool vis[maxn];
int cnt;
struct tumplex{
	int fir,sec,thi;
	tumplex(){}
	tumplex(int a,int b,int c):fir(a),sec(b),thi(c){}
};

vector<tumplex>vec[maxn],que[maxn];
int ans[maxn];

struct BIT{
	#define lowbit(x) (x&-x) 
	int c[maxn];
	void Add(int x,int d){for(;x<=q;x+=lowbit(x))c[x]+=d;}
	int Query(int x){int res=0;for(;x;x-=lowbit(x))res+=c[x];return res;}
}T;

int tot;

int Sol(int x,ll w){
	int l=1,r=x,ans=0;
	while(l<=r){
		int mid=(l+r)>>1;
		if(T.Query(mid)>=w)ans=mid,r=mid-1;
		else l=mid+1;
	}
	return ans;
}

void solve(){
	n=read(),q=read();int opt,x,y;
	ll w;
	Rep(i,1,q){
		opt=read();
		if(opt==1){
			x=read(),y=read(),w=readL();
			chk[++chk[0]]=i;
			vec[x].emplace_back(chk[0],w,i),vec[y+1].emplace_back(chk[0],-w,i);
		}else{ x=read(),w=readL();que[x].emplace_back(++tot,w,i); }
	}
	Rep(i,1,n){
		for(auto it : vec[i])T.Add(it.thi,it.sec);
		for(auto it : que[i])ans[it.fir]=Sol(it.thi,it.sec);
	}
	Rep(i,1,tot)printf("%d\n",ans[i]);
}
#undef int
int main (){ return solve(),0; }

T4排水系统

其实挺简单的,主要是想明白转移的意义。不难设出经过断边和没经过断边两种状态,发现我们必须要一条边断掉,于是我们在不断边的基础上进行流的修正即可。

点击查看代码


#include <bits/stdc++.h>
typedef long long ll;typedef unsigned long long ull; typedef double db;typedef long double ldb;
#define fre(x) freopen(#x ".in","r",stdin),freopen(#x ".out","w",stdout)
#define Rep(i,a,b) for(int i=a;i<=b;++i) 
#define Dwn(i,a,b) for(int i=a;i>=b;--i)
#define pii pair<int,int>
#define mair make_pair
#define fir first
#define sec second
using namespace std;

const int maxn=2e5+10,maxm=5e5+10,Mod=998244353;

int pw(int x,int p){int res=1,base=x;while(p){if(p&1)res=1LL*res*base%Mod;base=1LL*base*base%Mod;p>>=1;}return res;}
int Inv(int x){return pw(x,Mod-2);}

int f[maxn][2];
int n,m,r,k;
int inv[maxm];
int nowany,nownone,P,iP;

struct Graph{
	struct eg{int from,to,next,a,p;}e[maxm];
	int len,head[maxn];int ins[maxn],ous[maxn],vis[maxn];
	void lqx(int from,int to,int a)
	{ ++ins[to],++ous[from];e[++len].from=from,e[len].to=to,e[len].next=head[from],e[len].a=a,head[from]=len; }
	queue<int>q;
	void TopoDp(){
		Rep(i,1,len)e[i].p=1LL*e[i].a*iP%Mod;
		Rep(i,1,m)q.push(i),f[i][0]=f[i][1]=1;
		while(!q.empty()){
			int u=q.front();q.pop();
			cerr<<u<<" \n";
			int nany=0;
			for(int i=head[u];i;i=e[i].next){
				int v=e[i].to;
				nany=(nany+e[i].p)%Mod,++vis[v];
				if(vis[v]==ins[v])q.push(v);
			}
			for(int i=head[u];i;i=e[i].next){
				int v=e[i].to;
				f[v][0]=(f[v][0]+1LL*f[u][0]*inv[ous[u]])%Mod;
				f[v][1]=(f[v][1]+1LL*f[u][0]*inv[ous[u]]%Mod*(nany-e[i].p+Mod)%Mod*inv[ous[u]-1])%Mod;
				f[v][1]=(f[v][1]+1LL*f[u][1]*inv[ous[u]])%Mod;
				f[v][1]=(f[v][1]+1LL*(Mod-1)*f[u][0]%Mod*inv[ous[u]]%Mod*e[i].p)%Mod;
			}
		}
	}
}G;

void solve(){
	cin>>n>>m>>r>>k;int x,y,w;inv[0]=inv[1]=1;
	Rep(i,2,k)inv[i]=1LL*(Mod-Mod/i)*inv[Mod%i]%Mod;
	Rep(i,1,k)cin>>x>>y>>w,P=(P+w)%Mod,G.lqx(x,y,w);
	iP=Inv(P);G.TopoDp();
	Rep(i,n-r+1,n)cout<<f[i][1]<<" ";
}

int main (){ ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);return solve(),0; }

标签:typedef,int,题解,Rep,long,集训,maxn,CSP,define
来源: https://www.cnblogs.com/Delov/p/16661553.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有