3 25 省选模拟赛

(状态不太好 晚上很晚才睡。

T1:对于两个区间(a,b) (c,d) 若c<a<d 或 c<b<d 那么就可以从(a,b) 走到(c,d).LINK:CF上某题

n个操作 每次加入一个区间 或者 询问两个区间是否可达 初始集合为空 不强制在线,保证后加入的边比先加入的边长。(nleq 1e5)

暴力直接n^2连边 bfs判断。考虑优化。当然存在性质了。首先我们可以发现 对于任意两个集合都存在三种:
之间存在单向边 之间存在双向边 之间没有关系。一个显然的结论 答案要么直接通过一条单项边到达 要么通过双向边到达。

我们主要解决双向边怎么缩点(也就是连边问题。

可以发现我们可以线段树优化建图。但是如何满足条件?对于一个区间(a,b) 我们在a+1,b-1这段区间下方标记。

对于一个区间(c,d) 我们单调查找包含c,d所有区间的标记 那些标记都是可以连边的我们直接并查集缩点即可。

可以发现这样做是满足题目中的条件的,因为后来的比前面的长 所以不可能是包含关系。只有双向边的关系。

对于每条线段都这样做 这样复杂度可能会很高 因为每次我们都要把之前的标记给看一遍 这是不必要的 考虑缩过点之后我们直接拿父亲标记顶替以前的标记这样复杂度就不会退化。 注意也要同时更新父亲的区间大小 因为最后还有双向边的判断。

不得不说 这个线段树建图很精辟。

存在一个细节 单向边的关系 不仅只有包含的时候 当一个区间长度为2 的时候 这个时候包含端点但是不是互达的 这点要注意。

const int MAXN=200010;
int n,cnt,top,num;
int b[MAXN];
int f[MAXN],cl[MAXN],cr[MAXN];
vector<int>g[MAXN<<2];
struct wy
{
	int op;
	int x,y;
}t[MAXN];
inline void discrete()
{
	sort(b+1,b+1+cnt);
	rep(1,cnt,i)if(i==1||b[i]!=b[i-1])b[++num]=b[i];
	rep(1,n,i)if(t[i].op==1)
	{
		t[i].x=lower_bound(b+1,b+1+num,t[i].x)-b;
		t[i].y=lower_bound(b+1,b+1+num,t[i].y)-b;
	}
}
inline int getfather(int x){return x==f[x]?x:f[x]=getfather(f[x]);}
inline void change(int p,int l,int r,int x,int w)
{
	if(g[p].size())
	{
		rep(0,g[p].size()-1,i)
		{
			int tn=getfather(g[p][i]);
			f[tn]=w;cl[w]=min(cl[tn],cl[w]);cr[w]=max(cr[w],cr[tn]);
		}
		g[p].clear();g[p].pb(w);
	}
	if(l==r)return;
	int mid=(l+r)>>1;
	if(x<=mid)change(zz,l,mid,x,w);
	else change(yy,mid+1,r,x,w);
}
inline void modify(int p,int l,int r,int L,int R,int x)
{
	if(L<=l&&R>=r){g[p].pb(x);return;}
	int mid=(l+r)>>1;
	if(L<=mid)modify(zz,l,mid,L,R,x);
	if(R>mid)modify(yy,mid+1,r,L,R,x);
}
int main()
{
	freopen("1.in","r",stdin);
	freopen("1.out","w",stdout);
	get(n);
	rep(1,n,i)
	{
		get(t[i].op);get(t[i].x);get(t[i].y);
		if(t[i].op==1)b[++cnt]=t[i].x,b[++cnt]=t[i].y;
	}
	discrete();
	rep(1,n,i)
	{
		if(t[i].op==1)
		{
			++top;f[top]=top;
			cl[top]=t[i].x;
			cr[top]=t[i].y;
			change(1,1,num,t[i].x,top);
			change(1,1,num,t[i].y,top);
			if(t[i].x+1<t[i].y)modify(1,1,num,t[i].x+1,t[i].y-1,top);
		}
		else
		{
			int x=getfather(t[i].x);
			int y=getfather(t[i].y);
			if(x==y||(cl[y]<cl[x]&&cl[x]<cr[y])||(cl[y]<cr[x]&&cr[x]<cr[y]))puts("YES");
			else puts("NO");
		}
	}
	return 0;
}

T2:给出一个排列的置换的平方 求原排列 (nleq 1e6)LINK:CF上某题

这道题是送命题 很容易发现我们置换的平方 对于原本的序列来说 一个置换的大小为s s为奇数显然置换不变 s为偶数 显然置换分成两个环 gcd(s,2)=2...

考虑s为奇数 我们需要构造一下原序列 都让两个奇数s拼起来也行 但是 这是不必要的。

一种构造方法:暴力进行轮换 就可以知道上一次排列是谁 复杂度n^2.考虑直接构造出来 找到这个置换s 前s/2+1放后面 后s/2放前面即可。

考虑s为偶数 显然时两两置换配对 这个构造需要点技术 无解情况不再赘述。

我们知道一个数字连向了另一个数字那么就是一个数字可以控制另一个数字 一种连边方法(1,1)->(2,1)->(1,2)->(2,2)...

可以发现这样构造即可。

//#include<bitsstdc++.h>
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<cctype>
#include<cstdlib>
#include<queue>
#include<deque>
#include<stack>
#include<vector>
#include<algorithm>
#include<utility>
#include<bitset>
#include<set>
#include<map>
#define ll long long
#define db double
#define INF 1000000000
#define ld long double
#define pb push_back
#define gt(x) scanf("%d",&x)
#define putl(x) printf("%lld
",x)
#define gc(a) scanf("%s",a+1);
#define rep(p,n,i) for(RE int i=p;i<=n;++i)
#define go(x) for(int i=lin[x],tn=ver[i];i;tn=ver[i=nex[i]])
#define pii pair<int,int> 
#define F first
#define S second
#define mk make_pair
#define P 13331int
#define mod 1000000007
#define RE register
#define EPS 1e-4
using namespace std;
char buf[1<<15],*fs,*ft;
inline char getc()
{
	return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
	RE int x=0,f=1;char ch=getc();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
	return x*f;
}
const int MAXN=1000010;
int n,cnt;
int c[MAXN],a[MAXN],vis[MAXN],ans[MAXN];
vector<int>b[MAXN],w[MAXN];
inline void get(int x)
{
	rep(0,b[x].size()-1,i)a[i+1]=b[x][i];
	int mid=(b[x].size()>>1)+1;
	ans[a[mid]]=a[1];
	for(int i=1;i<mid;++i)
	{
		ans[a[i]]=a[mid+i];
		ans[a[mid+i]]=a[i+1];
	}
}
inline void get(int x,int y)
{
	rep(0,b[x].size()-1,i)
	{
		ans[b[y][i]]=b[x][i];
		ans[b[x][i]]=i==b[x].size()-1?b[y][0]:b[y][i+1];
	}
}
inline void put(int x)
{
	if(!x)return;
	put(x/10);
	char ch=x%10;
	putchar(ch);
}
int main()
{
	freopen("1.in","r",stdin);
	//freopen("1.out","w",stdout);
	db cl=clock();
	gt(n);
	rep(1,n,i)gt(c[i]);
	rep(1,n,i)
	{
		if(vis[i])continue;
		vis[i]=1;int x=c[i],cc=1;
		++cnt;b[cnt].pb(x);
		while(!vis[x])
		{
			vis[x]=1;x=c[x];
			b[cnt].pb(x);++cc;
		}
		w[cc].pb(cnt);
	}
	db cr=clock();
	cout<<cr-cl<<endl; 
	rep(1,n,i)
	{
		if(!w[i].size())continue;
		if(!(i&1))if(w[i].size()&1){puts("-1");return 0;}
		if(w[i].size()&1)
			rep(0,w[i].size()-1,j)get(w[i][j]);
		else
			for(int j=0;j<w[i].size()-1;j+=2)get(w[i][j],w[i][j+1]);
	}
	rep(1,n,i)put(ans[i]),putchar(' ');
	return 0;
}

T3:n个点给出每个点一个ru[i] 求出一棵大小为s有标号无根树 且每个点的度数不超过ru[i]的方案数。

原文地址:https://www.cnblogs.com/chdy/p/12568684.html