NOIP模拟二

炸了,连暴力都不会写……思维的劣势展现出来了……

link

T1

猜一堆结论的分类讨论题,过了就好。

按我的想法,首先构造出来的树中的三度点有一个有三个儿子,剩下的都是两个儿子。一度点都是叶子。那么就先把所有的一度点分给这些三度点,然后新建一个点把这些剩下的一度点连完。

然后是细节,如果三度点个数加二大于一度点个数那么无解,如果 a=0,b=3 无解,如果剩下一度点数为 1 无解,连点的时候是将一个一度点转化成一个 x 度点,所以新增的点也是 x 个。

code

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=114514,M=1919810;
ll a,b;
int main(){
	cin>>a>>b;
	swap(a,b); //三、一
	if(b==0){
		if(a==0) cout<<1;
		else cout<<0;
		return 0;
	}
	if(a==0&&b==3){
		cout<<0;
		return 0;
	}
	if(3+a-1>b){
		cout<<0;
		return 0;
	}
	/*if(b-a==3||b-a==4){
		cout<<0;
		return 0;
	}*/
	ll x,n;
	if(a>0) x=b-(3+a-1);
	else x=b;
	if(x==1){
		cout<<0;
		return 0;
	}
	n=a+b+(x>0?1:0);
	cout<<n<<'\n';
	ll tot=1;
	if(a==0){
		for(int i=2;i<=n;++i)
			cout<<1<<" "<<i<<'\n';
		return 0;
	}
	for(int i=1;i<=a;++i){
		if(i==1){
			cout<<tot<<" "<<tot+1<<'\n';
			cout<<tot<<" "<<tot+2<<'\n';
			cout<<tot<<" "<<tot+3<<'\n';
			tot+=3;
		}
		else{
			cout<<tot<<" "<<tot+1<<'\n';
			cout<<tot<<" "<<tot+2<<'\n';
			tot+=2;
		}
	}
	if(x>1){
		for(int i=tot+1;i<=n;++i)
			cout<<tot<<" "<<i<<'\n';
	}
	return 0;
}

T2

想到二分不会写……

想二分的时候思路就错了,其实有环的话直接不管他就好了,因为形成环的那条边对当前路径的答案其实是没有影响的,而且我们只需要知道二分出来一个答案之后能否从 1 走到 n 而不去管它的具体路径,所以我们设 dis[u] 为从起点走到 u 经过了几条边,直接广搜然后在 (dis[u]+1)w<=mid 并且未访问过 v 的时候直接更新 dis[v] 就行了。这个题的思路其实很简单,但是我一开始主要就是被环的问题给卡住了,应该多分析的。哎……

code

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=3*114514,M=1919810,inf=1145141919810;
struct xx{
	ll next,to,val;
}e[2*N];
ll head[2*N],cnt;
void add(ll x,ll y,ll z){
	e[++cnt].next=head[x];
	e[cnt].to=y;
	e[cnt].val=z;
	head[x]=cnt;
}
ll n,m,dis[N];
bool check(ll val){
	for(int i=1;i<=n;++i) dis[i]=inf;
	queue <ll> q;
	q.push(1); dis[1]=0;
	while(!q.empty()){
		ll u=q.front(); q.pop();
		for(int i=head[u];i;i=e[i].next){
			ll v=e[i].to,w=e[i].val;
			if(dis[v]>=inf&&(dis[u]+1)*w<=val){
				dis[v]=dis[u]+1;
				q.push(v);
			}
		}
	}
	return dis[n]<inf;
}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=m;++i){
		ll a,b,c;
		cin>>a>>b>>c;
		add(a,b,c);
	}
	ll l=1,r=3e14,ans=0; //从3e14开始分就行了 
	while(l<=r){
		ll mid=l+r>>1;
		if(check(mid)) r=mid-1,ans=mid;
		else l=mid+1;
	}
	cout<<ans;
	return 0;
}

T3

给边建边¿并查集¿最大生成树¿

是这样的。我们先从树的情况入手:当数据是一颗偶数个点的树时,通过模拟可以发现奇数点数总为 n,因为不管是哪一个点都可以通过选或不选连他父亲的边调整奇偶。如果是奇数个点,我们想要使一个点变为奇点就要使另一个奇点变成偶点,就将这两个点路径上的边都取反,而一条边能否被取反取决于他到根的第一条编号比自己小的边有没有取反并且他自己取反以后是不是让答案更大。那么对于每条边就向上连一条编号比它小的边,如果不存在就连一个虚空节点 0。最后从 0 开始走,每次走编号最小的能够flip的点。以上都可以用冰茶姬维护,简单一些。

对于不是树的情况,考虑取这个图的最大生成树然后在上面进行上述操作。为什么?题解曰:对于不会修改的边,他会永远是 1,所以最希望的是编号小的边他一直不修改,即这个边不在树上。你希望你会修改的边的编号最小值最大,有点类似与最小瓶颈路。所以你会选最大生成树。

code

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=1145140,M=1919810;
ll n,m,f[N],size[N],flag[N];
ll edge[N][2],x[N],y[N],cnt;
bool ans[N];
ll find(ll x){
	return x==f[x]?x:find(f[x]);
	//这题不能路径压缩!!!!! 
}
bool merge(ll x,ll y){
	x=find(x),y=find(y);
	if(size[x]>size[y]) swap(x,y);
	if(x!=y){
		++cnt;
		edge[cnt][0]=x,edge[cnt][1]=y;
		f[x]=y;
		size[y]+=size[x];
		return 1;
	}
	return 0;
}
void flip(ll x){
	while(x){
		++size[x];
		if(x==f[x]) return;
		x=f[x];
	}
}
bool deleted(ll u,ll v){
	ll x=edge[cnt][0],y=edge[cnt][1];
	--cnt;
	size[y]-=size[x];
	f[x]=x;
	if((size[x]&1)||(size[y]&1)){
		flip(u),flip(v);
		return 1;
	}
	return 0;
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;++i) f[i]=i,size[i]=1;
	for(int i=1;i<=m;++i) cin>>x[i]>>y[i],++x[i],++y[i];
	for(int i=m;i>=1;--i) flag[i]=merge(x[i],y[i]);
	for(int i=1;i<=m;++i)
		if(flag[i]) ans[i]=deleted(x[i],y[i]);
		else flip(x[i]),flip(y[i]),ans[i]=1;
	for(int i=1;i<=m;++i) cout<<ans[i];
	return 0;
}

T4

智慧 O(nlogn) 扫描线,还是算了吧……

直接抄题解了:
回顾矩形面积并的做法,区间加减维护 min 以及 mincnt。如果离线,考虑扫描线 x 轴,每次查询一个区间里每个位置历史上有多少次是 0。线段树上只需要维护 min,mincnt,ans 即可,为了支持修改还需要维护区间整体加,以及区间最小值位置上经过了多少长为 0 的时间。每次下放标记的时候,只有子区间里的 min 和整个区间的 min 相等才会更新子区间的 ans,可以理解为这是一个只对区间 min 的位置打的标记。如果儿子区间的 min 不是我的区间 min 自然就不会下传。

如果需要在线,第一步一定是把刚刚扫描线的过程可持久化了,每次在某个特定版本上查询。但是由于你无法离散化询问的端点,也就意味着你可能不存在刚刚好的版本。但是你可以通过把 [x1,x2] 分裂成:x1 下一个版本到 x2 上一个版本,x1 到下一个版本的这个后缀,x2 到前一个版本的前缀。对于后两者,因为矩形分布是均匀的,于是可以认为是前后两个版本减一下然后乘一个系数。对于 y 做同样的拆分,于是可以转化成 9 次边界都是离散化过的端点的矩形查询。注意询问可以标记永久化,来减少空间常数。时间复杂度 O(nlogn)

抄题解code

点击查看代码
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
using i128 = __int128_t;
constexpr int maxm = 3e7;
vector<int> sx, sy;
struct Segment {
	struct Node {
		int mn, mncnt, ls, rs;
		int del, tag;
		i64 mnsum;
	}a[maxm];
	struct info {
		int mn, mncnt; i64 mnsum;
		friend info operator + (info x, info y) {
			info z;
			z.mn = min(x.mn, y.mn); z.mnsum = x.mnsum + y.mnsum;
			if(x.mn < y.mn) z.mncnt = x.mncnt;
			else if(x.mn > y.mn) z.mncnt = y.mncnt;
			else z.mncnt = x.mncnt + y.mncnt;
			return z;
		}
		info apply(int del, int tag, int is) {
			return {mn + del, mncnt, mnsum + (mn + del == is ? 1ll * tag * mncnt: 0) };
		}
	};
	int cnt;
	inline int newnode(int u) {
		int cur = ++ cnt;
		assert(cnt < maxm - 10);
		a[cur] = a[u];
		return cur;
	}
	inline void seta(int u, int del, int tag, int is) {
		a[u].del += del;
		a[u].mn += del;
		if(a[u].mn == is) {
			a[u].mnsum += 1ll * a[u].mncnt * tag;
			a[u].tag += tag;
		}
	}
	inline void dw(int u) {
		a[u].ls = newnode(a[u].ls);
		seta(a[u].ls, a[u].del, a[u].tag, a[u].mn);
		a[u].rs = newnode(a[u].rs);
		seta(a[u].rs, a[u].del, a[u].tag, a[u].mn);
		a[u].del = a[u].tag = 0;
	}
	inline void up(int u) {
		a[u].mn = min(a[a[u].ls].mn, a[a[u].rs].mn);
		if(a[a[u].ls].mn < a[a[u].rs].mn) a[u].mncnt = a[a[u].ls].mncnt;
		else if(a[a[u].ls].mn > a[a[u].rs].mn) a[u].mncnt = a[a[u].rs].mncnt;
		else a[u].mncnt = a[a[u].ls].mncnt + a[a[u].rs].mncnt;
	}
	void update(int &u, int l, int r, int ql, int qr, int k) {
		u = newnode(u);
		if(l >= ql && r <= qr) return seta(u, k, 0, 0);
		int mid = l + r >> 1; dw(u);
		if(qr <= mid) update(a[u].ls, l, mid, ql, qr, k);
		else if(ql > mid) update(a[u].rs, mid + 1, r, ql, qr, k);
		else update(a[u].ls, l, mid, ql, qr, k), update(a[u].rs,mid + 1, r, ql, qr, k);
		up(u);
	}
	info query(int u, int l, int r, int ql, int qr) {
		if(l >= ql && r <= qr) return info{a[u].mn, a[u].mncnt, a[u].mnsum};
		int mid = l + r >> 1;
		if(qr <= mid) return query(a[u].ls, l, mid, ql, qr).apply(a[u].del, a[u].tag, a[u].mn);
		else if(ql > mid) return query(a[u].rs, mid + 1, r, ql, qr).apply(a[u].del, a[u].tag, a[u].mn);
		else return (query(a[u].ls, l, mid, ql, qr) + query(a[u].rs, mid + 1, r, ql, qr)).apply(a[u].del, a[u].tag, a[u].mn);
	}
	void build(int &u, int l, int r) {
		u = newnode(0);
		if(l == r) {
			a[u].mncnt = sy[l + 1] - sy[l];
			return ;
		}
		int mid = l + r >> 1;
		build(a[u].ls, l, mid), build(a[u].rs, mid + 1, r);
		up(u);
	}
}ds;
int main() {
//	freopen("in.txt", "r", stdin);
	ios::sync_with_stdio(false), cin.tie(0);
	int r, c, n, q;
	cin >> r >> c >> n >> q;
	vector<array<int, 4>> mat(n);
	for(int i = 0; i < n; i ++) {
		for(int j = 0; j < 4; j ++) cin >> mat[i][j];
		if(mat[i][0] == mat[i][2] || mat[i][1] == mat[i][3]) {
			i --, n --;
			continue;
		}
		if(mat[i][0] > mat[i][2]) swap(mat[i][0], mat[i][2]);
		if(mat[i][1] > mat[i][3]) swap(mat[i][1], mat[i][3]);
		sx.push_back(mat[i][0]), sx.push_back(mat[i][2]);
		sy.push_back(mat[i][1]), sy.push_back(mat[i][3]);
	}
	sx.push_back(-1), sy.push_back(-1);
	sx.push_back(r + 1), sy.push_back(c + 1);
	sort(sx.begin(), sx.end()), sx.erase(unique(sx.begin(), sx.end()), sx.end());
	sort(sy.begin(), sy.end()), sy.erase(unique(sy.begin(), sy.end()), sy.end());
	vector<vector<array<int, 3>>> upd(sx.size());
	vector<int> rt(sx.size());
	for(int i = 0; i < n; i ++) {
		for(int j = 0; j < 4; j ++) {
			if(j & 1) mat[i][j] = lower_bound(sy.begin(), sy.end(), mat[i][j]) - sy.begin();
			else mat[i][j] = lower_bound(sx.begin(), sx.end(), mat[i][j]) - sx.begin();
		}
		upd[mat[i][0]].push_back({mat[i][1], mat[i][3] - 1, 1});
		upd[mat[i][2]].push_back({mat[i][1], mat[i][3] - 1, -1});
	}
	ds.build(rt[0], 0, sy.size() - 2);
	for(int i = 1; i < sx.size(); i ++) {
		rt[i] = rt[i - 1];
		ds.seta(rt[i], 0, sx[i] - sx[i - 1], 0);
		for(auto u : upd[i]) ds.update(rt[i], 0, sy.size() - 2, u[0], u[1], u[2]);
	}
	i64 lstans = 0;
	int tot = 0;
	for(int i = 0; i < q; i ++) {
		int x1, x2, y1, y2;
		i64 v;
		cin >> x1 >> y1 >> x2 >> y2 >> v;
		x1 = (x1 + i128(v) * lstans) % (r + 1);
		x2 = (x2 + i128(v) * lstans) % (r + 1);
		y1 = (y1 + i128(v) * lstans) % (c + 1);
		y2 = (y2 + i128(v) * lstans) % (c + 1);
		if(x1 == x2 || y1 == y2) {
			cout << (lstans = 0) << '\n';
			continue;
		}
		if(x1 > x2) swap(x1, x2);
		if(y1 > y2) swap(y1, y2);
		int u1 = lower_bound(sx.begin(), sx.end(), x1) - sx.begin(), u2 = upper_bound(sx.begin(), sx.end(), x2) - sx.begin() - 1;
		int v1 = lower_bound(sy.begin(), sy.end(), y1) - sy.begin(), v2 = upper_bound(sy.begin(), sy.end(), y2) - sy.begin() - 1;
		auto calcy = [&] (int u1, int u2) {
			auto calc = [&] (int v1, int v2) {
				return ds.query(rt[u2], 0, sy.size() - 2, v1, v2).mnsum - (u1 ? ds.query(rt[u1 - 1], 0, sy.size() - 2, v1, v2).mnsum : 0);
			};
			if(v1 > v2) {
				i64 ans = calc(v2, v2) / (sy[v1] - sy[v2]) * (y2 - y1);
				return ans;
			}
			else {
				i64 ans = (sy[v1] - y1 ? calc(v1 - 1, v1 - 1) / (sy[v1] - sy[v1 - 1]) * (sy[v1] - y1) : 0) + 
								(y2 - sy[v2] ? calc(v2, v2) / (sy[v2 + 1] - sy[v2]) * (y2 - sy[v2]) : 0);
				if(v1 < v2) ans += calc(v1, v2 - 1);
				return ans;
			}
		};
		if(u1 > u2) {
			i64 ans =  calcy(u2, u2) / (sx[u1] - sx[u2]) * (x2 - x1);
			cout << (lstans = 1ll * (x2 - x1) * (y2 - y1) - ans) << '\n';
		}
		else {
			i64 ans = (sx[u1] - x1 ? calcy(u1 - 1, u1 - 1) / (sx[u1] - sx[u1 - 1]) * (sx[u1] - x1) : 0) + 
							(x2 - sx[u2] ? calcy(u2, u2) / (sx[u2 + 1] - sx[u2]) * (x2 - sx[u2]) : 0);
			if(u1 < u2) ans += calcy(u1, u2 - 1);
			cout << (lstans = 1ll * (x2 - x1) * (y2 - y1) - ans) << '\n';
		}
	}
	return 0;
}
posted @   和蜀玩  阅读(9)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示