CSP-S模拟37

T1: gcd&xor (gcdxor)

思路:

算错复杂度导致结束前 \(3 ~ min\) 临时加了个快读,差点把文件 \(IO\) 整错了,吓死个人...

首先,暴力大家肯定都会。

遇事不决暴力打表找规律。所以啥原理根本不懂,只能说是由表可得。

其实我觉得再从头打表找一遍规律意义不大,就不展开了。

代码:

$code1$
#include<iostream>
using namespace std;
int n,ans;
inline int gcd(int x,int y){
	if(!y) return x;
	return gcd(y,x%y);
}
int main(){
	ios::sync_with_stdio(false);
	cin>>n;
	for(int i=2;i<=n;i+=2){
		for(int j=n/(i+1);j>=1;j--){
			if(gcd(i*j,(i+1)*j)==((i*j)^((i+1)*j))) ans++;
		}
	}
	cout<<ans<<'\n';
	return 0;
}
$code2$
#include<iostream>
#define re register
using namespace std;
int n,sum,maxn;
inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch-'0');ch=getchar();}return x*f;}
inline void write(int x){if(x<0)x*=-1,putchar('-');if(x>9)write(x/10);putchar(x%10+'0');return;}
int main(){
//	freopen("gcdxor.in","r",stdin);
//	freopen("gcdxor.out","w",stdout);
	n=read();
	sum=(n-1)>>1;
	maxn=(n+2)/3;
	for(re int i=2;i<=maxn;i++){//枚举i,j的差值,即为gcd 
		for(re int j=2;j<n/i;j++){//最大能是多少倍 
			if(((j*i)^((j+1)*i))==i){
				sum++;
			}
		}
	}
	write(sum);
	return 0;
}

T2:异或树 (xortree)

思路:

显然无论如何进行操作 \(1\),非叶子结点都不会产生影响(废话,都不扩展非叶子结点 【记得读题哦】)。

所以我们可以分开统计叶子节点的贡献和非叶子结点的贡献,最后的答案加起来就好了。

我们设 \(f_i\) 表示权值为 \(i\) 的叶子节点的个数(其实就是叶子节点的子树异或和), \(g_i\) 表示子树异或和为 \(i\) 的非叶子结点个数。

转移就很显然啦。

每个节点都可以跟它的左子节点抵消掉,所以新生成的子树的异或和为它的右子节点的权值。

而每个节点跟它的左子节点相等,所以新增的叶子结点即为原叶子结点的新增右子节点。

我们假设一个叶子节点的权值为 \(t\) ,则

\[g_{ ~ t ~ ⊕ ~ x} ~ += f_{t} \]

\[f_{ ~ t ~ ⊕ ~ x} ~ += f_{t} \]

然后对着敲就好啦~~

代码:

$code$
#include<iostream>
#include<cstring>
#define int long long
using namespace std;
const int N=8888,mod=998244353,maxn=1<<13;
int k,q,op,x,f[N],g[N],h[N];
signed main(){
//	freopen("xortree.in","r",stdin);
//	freopen("xortree.out","w",stdout);
	ios::sync_with_stdio(false);
	cin>>k>>q;
	f[k]=1;
	while(q--){
		cin>>op>>x;
		if(op==1){
			memcpy(h,f,sizeof(f));
			for(int i=0;i<=maxn;i++){
				g[i]=(g[i]+h[i^x])%mod;
				f[i]=(f[i]+h[i^x])%mod;
			}
		}else cout<<(f[x]+g[x])%mod<<'\n';
	}
	return 0;
}

T3:符文石 (stone)

思路:

你当然可以效法某人用dfs硬创过去,好像还挺优?

首先,根据当代年轻人 \(200 ~ \%\) 的反骨,我们就要跟题目反着建边(嘿嘿,其实是反着建边便于操作)

由于正着建边所得到的点集为它所能到的点的集合,那反着建边就是能到它的点的集合咯。那我们就可以给它 \(topo\) 一下,然后再把集合合并一下就好啦。

那如何合并呢?

由于两个数按位与以后的结果一定小于等于这两个数中较小的一个,假设我们当前维护到的按位与的最大值为 \(maxn\) ,那么以后对答案产生贡献的的数一定大于等于 \(maxn\) ,而等于 \(maxn\) 的数最多保留两个。

综上,我们只需要两两合并集合的时候把集合中大于当前最大按位与的值的数和至多两个等于当前最大按位与的值的数存下来就好。

\(Last...\) 这次不能 #define int long long了,不然可能会爆空间的哇

代码:

$code$
#include<iostream>
#include<queue>
#define ll long long
using namespace std;
const int N=5e5+5;
int m,n,u,v,cnt,du[N],head[N];bool vis[N];
ll a[N];
struct node{int to,nxt;}e[N<<1];
struct flower{
	int cnt,id[65];
	ll maxn,val[65];
}p[N];
queue<int> q;
inline void add(int x,int y){
	e[++cnt].to=y;
	e[cnt].nxt=head[x];
	head[x]=cnt;
}
inline void merge(flower &x,flower y){
	flower res;
	res.maxn=max(x.maxn,y.maxn);
	res.cnt=0;
	for(int i=1;i<=x.cnt;i++){
		for(int j=1;j<=y.cnt;j++){
			if(x.id[i]!=y.id[j]) res.maxn=max(res.maxn,x.val[i]&y.val[j]);
		}
	}
	bool flag=0;
	for(int i=1;i<=x.cnt;i++){
		if(!vis[x.id[i]]){
			if(x.val[i]>res.maxn){
				res.id[++res.cnt]=x.id[i];
				res.val[res.cnt]=x.val[i];
				vis[x.id[i]]=1;
			}else if(x.val[i]==res.maxn&&!flag){
				flag=1;
				res.id[++res.cnt]=x.id[i];
				res.val[res.cnt]=x.val[i];
				vis[x.id[i]]=1;
			}
		}	
	}
	for(int i=1;i<=y.cnt;i++){
		if(!vis[y.id[i]]){
			if(y.val[i]>res.maxn){
				res.id[++res.cnt]=y.id[i];
				res.val[res.cnt]=y.val[i];
				vis[y.id[i]]=1;
			}else if(y.val[i]==res.maxn&&!flag){
				flag=1;
				res.id[++res.cnt]=y.id[i];
				res.val[res.cnt]=y.val[i];
				vis[y.id[i]]=1;
			}
		}	
	}
	for (int i=1;i<=x.cnt;i++) vis[x.id[i]]=0;
    for (int i=1;i<=y.cnt;i++) vis[y.id[i]]=0;
    x=res;
}
signed main(){
//	freopen("stone.in","r",stdin);
//	freopen("stone.out","w",stdout);
	ios::sync_with_stdio(false);
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>a[i];
	while(m--){
		cin>>u>>v;
		add(v,u);
		du[u]++;
	}
	for(int i=1;i<=n;i++){
		p[i].cnt=1;
		p[i].maxn=-1;
		p[i].id[1]=i;
		p[i].val[1]=a[i];
	}
	for(int i=1;i<=n;i++) if(!du[i]) q.push(i);
	while(!q.empty()){
		int x=q.front();q.pop();
		for(int i=head[x];i;i=e[i].nxt){
			int y=e[i].to;
			merge(p[y],p[x]);
			du[y]--;
			if(!du[y]) q.push(y);
		}
	}
	for(int i=1;i<=n;i++) cout<<p[i].maxn<<' ';
	return 0;
}

后言

闲话

为何说未来迷茫?

红星在远处照耀 ,共党在前方领导

为何说无路可走?

社会主义建设的道路就在脚下

为何说身后无人?

智慧的老祖宗,强大的祖国,十四万万的同胞都在你的身后

歌词

那一年你和我一样年纪

年轻得像首青涩的歌曲

但为了创造梦中那个新天地

你转身 匆匆走进风雨

我看见千万个可爱的你

不回头向硝烟深处奔去

多少个青春背影消失在夜里

换来晨曦

我仰望你看过的星空

穿过百年时空再相逢

你转过身之前的那个笑容

我都懂

我仰望你看过的星空

脚下大地已换了时空

你留在风中摇曳的那抹红

在心中 心中

举起手我说出同样誓言

黑白间你的笑容多清晰

你说你从来也不后悔把一生

奉献给 这片辽阔大地

我多想伸手紧紧拥抱你

告诉你一切都尘埃落定

百年前你梦想的那个新中国

有多美丽

我仰望你看过的星空

穿过百年时空再相逢

在此刻我们总会心灵相通

我都懂

我仰望你看过的星空

脚下大地已换了时空

你留在风中摇曳的那抹红

在心中 心中

我仰望你看过的星空

穿过百年时空再相逢

在此刻我们总会心灵相通

我都懂

我仰望你看过的星空

脚下大地已换了时空

你留在风中摇曳的那抹红

在心中 心中

posted @ 2025-10-22 22:03  晏清玖安  阅读(40)  评论(6)    收藏  举报