CF 368 div 2(bitset/主席树/二维线段树)

比赛链接

C

输入三角形的一条边,输出另外两条可以和他组成一个直角三角形的整数边。

以前似乎做过,但是忘记了可以O\left ( 1 \right )的公式,但时间范围很长可以直接递归O\left ( \log n \right )

O\left ( 1 \right )公式:

a=2*k, ( k\epsilon Z),b=k^{2}-1,c=k^{2}+1

a=2*k+1,(k\epsilon Z),b=\frac{1}{2}(a^{2}-1), c=\frac{1}{2}(a^2+1)

#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<vector>
#include<set>
#include<math.h>
#include<queue>
#include<map>
#include<stack>
#include<deque>
#define go(i,a,b) for (int (i)=(a);(i)<=(b);(i)++)
#define ll long long
#define MOD 1000000007
#define N 100005
using namespace std;
ll y,z;
void work(ll x){
	if(x==4){
		y=3;
		z=5;
	}
	else if (x%2){
		y=(x*x)/2;
		z=(x*x)/2+1;
	}
	else{
		work(x/2);
		y*=2;
		z*=2;
	}
	return;
}
int main(){
	ll x;
	scanf("%lld",&x);
	if (x==1||x==2) printf("-1");
	else {
		work(x);
		printf("%lld %lld",y,z);
	}
} 

D

BITSET的一些妙用

刚开始想用线段树强解,发现dfs更新很麻烦,在网上看了一下大家的代码,用bitset就很方便了,因为只有0/1的状态,

用树状结构存储往回跳的步骤,最后dfs搜索即可,ans即时更新。

#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<vector>
#include<set>
#include<math.h>
#include<queue>
#include<map>
#include<stack>
#include<deque>
#include<bitset>
#define go(i,a,b) for (int (i)=(a);(i)<=(b);(i)++)
#define ll long long
#define MOD 1000000007
#define N 100005
using namespace std;
bitset<1005>b[1005];
bitset<1005>c;
int n,m,Q,p[N],tot=0,head[N];
struct edge{
	int v, next;
}e[N*2]; 
struct op{
	int x,i,j;
}q[N];
void addedge(int u, int v){
	e[++tot]=(edge){v,head[u]};
	head[u]=tot;
}
void dfs(int now, int ans){
	if (now==Q+1) return;
	if (q[now].x==1){
		int tmp=b[q[now].i][q[now].j];
		if (!tmp) b[q[now].i][q[now].j]=1, ans++;
		p[now]=ans;
		for (int i=head[now];i;i=e[i].next){
			dfs(e[i].v,ans);
		}
		if (!tmp) b[q[now].i].reset(q[now].j),ans--;
	}
	else if (q[now].x==2){
		int tmp=b[q[now].i][q[now].j];
		if (tmp) b[q[now].i][q[now].j]=0, ans--;
		p[now]=ans;
		for (int i=head[now];i;i=e[i].next){
			dfs(e[i].v,ans);
		}
		if (tmp) b[q[now].i][q[now].j]=1,ans++;
	}
	else if (q[now].x==3){
		int cnt=b[q[now].i].count();
		b[q[now].i]^=c;
		ans=ans-cnt+(m-cnt);
		p[now]=ans;
		for (int i=head[now];i;i=e[i].next){
			dfs(e[i].v,ans);
		}
		b[q[now].i]^=c;
		ans=ans-(m-cnt)+cnt;
	}
	else if (q[now].x==4){
		p[now]=ans;
		for (int i=head[now];i;i=e[i].next){
			dfs(e[i].v,ans);
		}
	}
}
int main(){
	int k;
	scanf("%d%d%d",&n,&m,&Q);
	go(i,1,m) c[i]=1;
	q[0].x=4;
	go(i,1,Q){
		scanf("%d",&q[i].x);
		if (q[i].x==1||q[i].x==2)scanf("%d%d",&q[i].i,&q[i].j);
		else if (q[i].x==3) scanf("%d",&q[i].i);
		
		if (q[i].x==4){
			scanf("%d",&k);
			addedge(k,i);
		}
		else addedge(i-1,i);
	}
	dfs(0,0);
	go(i,1,Q){
		printf("%d\n",p[i]);
	}
} 

E

比赛的时候并没有思路,感觉二维线段树肯定会超时,然后滚去上课。

然后查题解发现并不会,然后看到一个题解是用主席树写的,(于是学习了一下主席树,创新性的给自己写了一个非递归模板,这题根本用不了,只好重新写了一个递归的)。

题目的思路主要是:

1.先把每个garland的点都读进一个vector保存,sort成以x为第一关键字的顺序序列

2.离线处理询问,将询问按顺序读入,对于每个询问,记录当前garland开关的状态(处理方式是记录每个garland会对哪些询问做出贡献,而非直接记录状态)

3.利用主席树对于每个garland(的y)建立主席树,然后利用主席树的结构限制x的范围,达到二维的目的(666)

以及has[i].size()出现了一些bug,size的形式是unsigned int,-1之后就是无穷大了,注意。

关于vector的size的一些讲解

#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<vector>
#include<set>
#include<math.h>
#include<queue>
#include<map>
#include<stack>
#include<deque>
#define N 2005
#define ll long long
#define go(i,a,b) for (ll i=a;i<=b;i++)
using namespace std;

struct place{
	int x,y;
	ll w;
	bool operator< (const place b){
        if (x==b.x) return y<b.y;
        return x<b.x;
	}
};
struct node{
	int ls,rs;
	ll sum;
}t[N*20];
struct query{
	int x1,y1,x2,y2,typ,i;
}q[1000005];
int top[N];
bool now[N];
vector<place>gar[N];
vector<int>has[N];
char s[20];
ll cnt,ans[1000005];
int updata(int pre, int l, int r, int pos, int c){
	ll rt=++cnt;
	t[rt]=t[pre];
	t[rt].sum+=c;
	if (l==r) return rt;
	int m=(l+r)/2;
	if (pos<=m) t[rt].ls=updata(t[pre].ls,l,m,pos,c);
	else t[rt].rs=updata(t[pre].rs,m+1,r,pos,c);
	return rt;
}
ll query(int rt1, int rt2, int l, int r, int left, int right){
	if (l>right||r<left) return 0;
	if (left<=l&&r<=right){
		return t[rt2].sum-t[rt1].sum;
	}
	int m=(l+r)/2;
	return query(t[rt1].ls,t[rt2].ls,l,m,left,right)+
	query(t[rt1].rs,t[rt2].rs,m+1,r,left,right);
}
int main(){
	int n,m,k,len,x,y,Q;
	ll w;
	while (scanf("%d%d%d",&n,&m,&k)!=EOF){

		go(i,1,k){
			now[i]=true;
			scanf("%d",&len);
			go(j,1,len){
				scanf("%d%d%lld",&x,&y,&w);
				gar[i].push_back((place){x,y,w});
			}
			sort(gar[i].begin(),gar[i].end());
		}

		scanf("%d",&Q);
		go(i,1,Q){
			scanf("%s",s);
			if (s[0]=='S'){
				scanf("%d",&q[i].i);
				q[i].typ=1;
				now[q[i].i]^=1;
			}
			else{
			    //cout<<"!"<<endl;
				scanf("%d%d%d%d",&q[i].x1,&q[i].y1,&q[i].x2,&q[i].y2);
				q[i].typ=2;
				go(j,1,k){
					if (now[j]) has[j].push_back(i);
				}
			}
		}
		go(i,1,k){
			cnt=0;
			go(j,0,gar[i].size()-1){
				top[j+1]=updata(top[j],1,m,gar[i][j].y,gar[i][j].w);
			}
			//cout<<i<<endl;
			for (int j=0;j<=has[i].size()-1;j++){
			    if (has[i].size()==0) break;
				int tmp=has[i][j];
				int x1=q[tmp].x1,y1=q[tmp].y1,x2=q[tmp].x2,y2=q[tmp].y2;
				place temp=(place){x2,m+1};
				int pos=lower_bound(gar[i].begin(),gar[i].end(),temp)-gar[i].begin();
				if (pos) ans[tmp]+=query(0,top[pos],1,m,y1,y2);
				temp=(place){x1,0};
				pos=lower_bound(gar[i].begin(),gar[i].end(),temp)-gar[i].begin();
				if (pos) ans[tmp]-=query(0,top[pos],1,m,y1,y2);
			}
		}
		go(i,1,Q){
			if (q[i].typ==2) printf("%lld\n",ans[i]);
		}
	}
	return 0;
}

 

这是一个非常好的问题!你问到了 C++ 中一个关键的设计思想:**`std::bitset` 本身不是二维数组,但我们可以通过创建多个 `bitset` 来模拟“二维关系”**。 --- ## ✅ 回答核心: > **我们并没有用一个 `bitset` 存储所有二维数据,而是为每一个节点(人)创建一个 `bitset`,用来表示它与其他所有节点的关系。** > > 多个这样的 `bitset` 组合起来,就等价于一个大的二维布尔矩阵。 --- ### 🔍 类比理解:邻接矩阵 vs 邻接 bitset 假设你想表示图中点与点之间的某种关系(比如朋友),传统做法是: ```cpp bool adj[1000][1000]; // adj[i][j] = true 表示 i 和 j 有关系 ``` 这是典型的二维数组,空间复杂度 O(n&sup2;)。 但在 n 很大时(如 3e4),你不能开 `bool adj[30000][30000]`,因为那需要: - `9e8` 个 `bool` → 约 900MB 每个数组 → 两个就是 1.8GB,接近极限 而我们可以换一种方式: ```cpp bitset<MAXN> rel[MAXN]; // rel[i][j] 表示 i 与 j 的关系 ``` 这相当于把原来的二维数组“按行拆分”: - `rel[0]` 是第 0 行 - `rel[1]` 是第 1 行 - ... - `rel[i][j]` 就是原矩阵中的 `adj[i][j]` 所以: > `rel[u][v] == true` ⇔ u 和 v 有某种关系(例如朋友) --- ### ✅ 实际例子 假设有 5 个人,我们知道: - 0 和 2 是朋友 - 0 和 3 是敌人 我们这样存储: ```cpp friends[0][2] = true; // 第0个人的朋友包括第2个 friends[2][0] = true; // 对称设置 enemies[0][3] = true; enemies[3][0] = true; ``` 此时: - `friends[0]` 是一个 bitset,其第 2 位为 1,其余为 0 - 它代表:“0 的朋友有哪些?” → {2} - 同理,`enemies[0]` 表示 “0 的敌人有哪些?” → {3} --- ## 🧠 关键优势:支持高效集合操作 这才是 `bitset` 的真正强大之处! 你想知道:是否存在一个人 w,既是 u 的朋友、又是 v 的敌人? 传统方法要循环遍历所有 w: ```cpp bool found = false; for (int w = 0; w < n; ++w) { if (friends[u][w] && enemies[v][w]) { found = true; break; } } ``` 而现在你可以写成: ```cpp if ((friends[u] & enemies[v]).any()) { // 存在交集 → 有破坏者 } ``` 👉 这一句 `(friends[u] & enemies[v])` 做了什么? - 取出 u 的朋友集合 - 取出 v 的敌人集合 - 按位与 → 得到共同成员集合 - `.any()` 判断是否非空 ✅ 时间复杂度从 O(n) 降到 **O(n / word_size)**,实际运行快几十倍! --- ## 💡 总结:为什么能“读入二维数据”? | 说法 | 解释 | |------|------| | ❌ `bitset` 是二维的? | 错!单个 `bitset<N>` 是一维的 | | ✅ 我们用了 `bitset<N> arr[M]` | 相当于 M × N 的二维布尔矩阵 | | ✅ `arr[i][j]` 可以快速访问 | 支持随机访问,语义同二维数组 | | ✅ 支持 `&`, `|`, `^`, `~`, `.any()`, `.count()` | 提供集合运算能力,远超普通数组 | --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值