省选模拟-Day2

关于教练拿17年联训题给我们做这件事。csp还没考这样真的好吗
整体难度中等偏上,区分度明显,然而显然时间不够
不多说,看题


十字形 cross

【问题描述】

有一个平面直角坐标系,上面有一些线段,保证这些线段至少与一条坐标轴
平行。我们需要计算出,这些线段构成的最大的十字型有多大。
称一个图形为大小为 R(R 为正整数)的十字型,当且仅当,这个图形具有
一个中心点,它存在于某一条线段上,并且由该点向上下左右延伸出的长度为 R
的线段都被已有的线段覆盖。
你可以假定:没有两条共线的线段具有公共点,没有重合的线段。

【输入格式】

从文件 cross.in 中输入数据。
第一行,一个正整数 N,代表线段的数目。
以下 N 行,每行四个整数 x1,y1,x2,y2(x1=x2 或 y1=y2),描述了一条线段。

【输出格式】

输出到文件 cross.out 中。
当不存在十字型时:输出一行“Human intelligence is really terrible”(不包括引号)。
否则:输出一行,一个整数,为最大的 R。

【样例输入 1】

1
0 0 0 1

【样例输出 1】

Human intelligence is really terrible

【样例输入 2】

3
-1 0 5 0
0 -1 0 1
2 -2 2 2

【样例输出 2】

2

【数据规模与约定】

对于 50%的数据:N≤1000。
对于 100%的数据:1≤N≤100000,所有坐标的范围在-109~109 中。
后 50%内,所有数据均为随机生成。

看到答案具有单调性,想到二分答案。
二分后每条线减去当前答案长度,判是否有相交线段。
线段分为平行于 X 轴和 Y 轴考虑,直接上扫描线判交。
标答用的线段树维护,实际直接用平衡树即可。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
inline int rd(){
	int f=1,j=0;
	char w=getchar();
	while(w>'9'||w<'0'){
		if(w=='-')f=-1;
		w=getchar();
	}
	while(w>='0'&&w<='9'){
		j=(j<<3)+(j<<1)+w-'0';
		w=getchar();
	}
	return f*j;
}
const int N=100001;
struct node1{
	int l,r,pla;
}X[N],Y[N];
struct node2{
	int t,pla,sum,l,r;
}p[N*5];
set<int>q;
int n,tx,ty,cnt;
inline bool cmp(node2 a,node2 b){
	if(a.pla!=b.pla)return a.pla<b.pla;
	return a.t<b.t;
}
bool pan(int k){
//	cout<<k<<":\n";
	q.clear(),cnt=0;
	for(int i=1;i<=tx;i++){
		int l=X[i].l+k,r=X[i].r-k;
		if(l>r)continue;
//		cout<<"have X-line\n";
		p[++cnt].t=1,p[cnt].sum=1,p[cnt].pla=l,p[cnt].l=X[i].pla;
		p[++cnt].t=1,p[cnt].sum=-1,p[cnt].pla=r+1,p[cnt].l=X[i].pla;
	}
	for(int i=1;i<=ty;i++){
		int l=Y[i].l+k,r=Y[i].r-k;
		if(l>r)continue;
		p[++cnt].t=2,p[cnt].l=l,p[cnt].r=r,p[cnt].pla=Y[i].pla;
	}
	sort(p+1,p+cnt+1,cmp);
	for(int i=1;i<=cnt;i++){
		if(p[i].t==1){
			if(p[i].sum==1)q.insert(p[i].l);
			else q.erase(p[i].l);
		}
		else{
			set<int>::iterator it=q.lower_bound(p[i].l);
//			if(it==q.end())printf("kk\n");
//			else cout<<*it<<endl;
			if(it!=q.end()&&(*it)<=p[i].r)return true;
		}
	}
//	cout<<"-----------------\n";
	return false;
}
signed main(){
//	freopen("cross.in","r",stdin);
//	freopen("cross.out","w",stdout);
	n=rd();
	for(int i=1;i<=n;i++){
		int x[2],y[2];
		for(int j=0;j<=1;j++)x[j]=rd(),y[j]=rd();
		if(y[0]==y[1])X[++tx].l=min(x[0],x[1]),X[tx].r=max(x[0],x[1]),X[tx].pla=y[0];
		else Y[++ty].l=min(y[0],y[1]),Y[ty].r=max(y[0],y[1]),Y[ty].pla=x[0];
	}
	int l=1,r=1e9,mid;
	while(l<=r){
		mid=(l+r)/2;
		if(pan(mid))l=mid+1;
		else r=mid-1; 
	}
	if(!r)printf("Human intelligence is really terrible");
	else printf("%d",r);
	return 0;
}


集合 set

【问题描述】

小明非常喜欢数字。最近他的妈妈送给他了一个由 n 个非负整数组成的集
合。小明非常喜欢和小刚玩。他立即决定把他 n 个数字中的一部分送给小刚。为
了让游戏更加有趣,小明决定使得给她的数字集合满足如下条件:
我们用 x1 表示小明的数字集合的 xor 值,用 x2 表示小刚的数字集合的 xor
值。要使得 x1+x2 尽可能地大。假如有多种划分集合的方法使得集合满足上述条
件,小明就要让 x1 尽可能地小。
帮助小明 set 照上述方法划分集合。如果有多种合适的方法,就找出其中任
意一种方法。请注意,小明将一部分数字给了小刚之后,他可能就没有任何剩余
的数字了。反之亦然,小明也可以不给小刚任何数字。在这两种情况下,我们都
假定空集的 xor 值为 0。

【输入格式】

从文件 set.in 中输入数据。
输入的第一行包含一个整数 n,表示小明的妈妈给了他多少个数字。
第二行包含 n 个用空格隔开的数字,保证它们都是不超过 10^18 的非负整数。

【输出格式】

输出到文件 set.out 中。
输出一行,包含 n 个用空格隔开的整数,如果第 i 个数是 1,则表示小明保
留给出的第 i 个数字,如果第 i 个数是 2,则表示小明把给出的第 i 个数字给了小
刚。

【样例输入】

8
1 1 2 2 3 3 4 4

【样例输出】

1 2 1 2 2 2 1 2

【数据规模与约定】

对于 30%的数据,保证 n<=10;
对于 60%的数据,保证 n<=1000;
对于 100%的数据,保证 n<=100000。

复习好久没写的高消和线性基。
此题要求最大异或和,显然从高往低按位贪心。
对于所有值的异或和X,如果当前第i位为1,当前位对答案贡献只能为2i
如果当前为0,可以拆作1^1,对答案贡献22i
可以得到log个异或方程,即 (t1×x1)(t2×x2)(tn×xn)=tX,i1
t表示当前数的当前位是否为1。
线性基解即可。
同时要满足 X1 尽可能小,对于所有的异或方程,右边为1的直接给方程中最左的数划到 X1 中,为0的不管。
代码就不放了。


通信系统 communicate

【问题描述】

在 n 个城市建立起一套通信系统。n 个城市两两之间有且仅有一条简单路径。
一个通信系统的选择方案是随机选择一段连续序号的点,方案的代价为从被选择
的点中选择任意一个点,从该点出发遍历所有被选择点,并回到出发点的总路径。
现在,你的任务就是求出通信系统代价的期望值。(对 1000000007 取模)

【输入格式】

从文件 communicate.in 中输入数据。
输入的第一行包含一个整数 n,表示城市的数量。
第 2 至 n 行每行两个整数 x,y,表示在 x 和 y 之间连一条边。

【输出格式】

输出到文件 communicate.out 中。
输出一个整数,表示期望值。

【样例输入】

10
2 1
3 2
4 3
5 3
6 5
7 1
8 2
9 3
10 6

【样例输出】

490909103

【数据规模与约定】

对于 20%的数据,n<=100
对于 40%的数据,n<=1000
对于 60%的数据,n<=5000
对于 80%的数据,n<=30000
对于 100%的数据,n<=100000

树上遍历选中的点?这不就是虚树吗?
对于每一种情况把虚树建出来,树上所有边权和乘2就是答案。
但是这样不太好优化,考虑每条边对答案的贡献。
对于一棵子树,将子树内节点全部标记为黑色,其余标记为白色,当选取的区间同时包含黑色和白色,答案++。
正难则反,将总选取次数减去区间只包含单色的选取次数,即为当前子树根到它的父亲这条边对答案的贡献。
可用线段树维护,启发式合并,复杂度 nlog2n

点击查看代码
#include<bits/stdc++.h>
using namespace std;
inline int rd(){
	int f=1,j=0;
	char w=getchar();
	while(w>'9'||w<'0'){
		if(w=='-')f=-1;
		w=getchar();
	}
	while(w>='0'&&w<='9'){
		j=(j<<3)+(j<<1)+w-'0';
		w=getchar();
	}
	return f*j;
}
const int N=100001,mod=1000000007;
struct node{
	int sum,ls,rs,col_l,col_r,len_l,len_r;
}tr[N*50];
int head[N],to[N*2],fro[N*2],tail;
int root[N],siz[N],n,ans,cnt;
inline void addline(int x,int y){
	to[++tail]=y;
	fro[tail]=head[x];
	head[x]=tail;
	return ;
}
inline void whi(int l,int r){
	int len=r-l+1;
	tr[0].sum=1ll*len*(len-1)/2%mod;
	tr[0].col_l=tr[0].col_r=0;
	tr[0].len_l=tr[0].len_r=len;
	return ;
}
inline int LEN(int x,int y){return y-x+1;}
void update(int u,int L,int R){
	int l=tr[u].ls,r=tr[u].rs;
	if(!l)whi(L,(L+R)/2);
	if(!r)whi((L+R)/2+1,R);
	tr[u].sum=tr[l].sum+tr[r].sum;
	tr[u].sum%=mod;
	if(tr[l].col_r==tr[r].col_l)tr[u].sum+=1ll*tr[l].len_r*tr[r].len_l%mod,tr[u].sum%=mod;
	tr[u].col_l=tr[l].col_l;
	tr[u].col_r=tr[r].col_r;
//	cout<<l<<"::::"<<tr[l].len_l<<" "<<L<<" "<<R<<" "<<LEN(L,(L+R)/2)<<"||"<<tr[l].len_l<<" "<<tr[r].col_l<<endl;
	if(tr[l].len_l==LEN(L,(L+R)/2)&&tr[r].col_l==tr[l].col_l)tr[u].len_l=tr[l].len_l+tr[r].len_l;
	else tr[u].len_l=tr[l].len_l;
	if(tr[r].len_r==LEN((L+R)/2+1,R)&&tr[r].col_r==tr[l].col_r)tr[u].len_r=tr[r].len_r+tr[l].len_r;
	else tr[u].len_r=tr[r].len_r;
	return ;
}
void add(int &u,int l,int r,int aim){
	if(!u)u=++cnt;
	if(l==r){
		tr[u].col_l=tr[u].col_r=1;
		tr[u].len_l=tr[u].len_r=1;
//		cout<<"add:"<<l<<" "<<r<<":"<<tr[u].col_l<<" "<<tr[u].len_l<<"|"<<tr[u].col_r<<" "<<tr[u].col_r<<":"
//			<<tr[u].sum<<endl;
		return ;
	}
	int mid=(l+r)/2;
	if(aim<=mid)add(tr[u].ls,l,mid,aim);
	else add(tr[u].rs,mid+1,r,aim);
	update(u,l,r);
//	cout<<"add:"<<l<<" "<<r<<":"<<tr[u].col_l<<" "<<tr[u].len_l<<"|"<<tr[u].col_r<<" "<<tr[u].col_r<<":"
//		<<tr[u].sum<<endl;
	return ;
}
void merge(int &u,int v,int l,int r){
	if(!v)return ;
	if(l==r){
		if(tr[v].col_l)u=v;
		return ;
	}
	if(!u){
		u=v;
		return ;
	}
	int mid=(l+r)/2;
	if(tr[v].ls)merge(tr[u].ls,tr[v].ls,l,mid);
	if(tr[v].rs)merge(tr[u].rs,tr[v].rs,mid+1,r);
	update(u,l,r);
//	cout<<"merge:"<<l<<" "<<r<<":"<<tr[u].col_l<<" "<<tr[u].len_l<<"|"<<tr[u].col_r<<" "<<tr[u].col_r<<":"
//		<<tr[u].sum<<endl;
	return ;
}
void dfs(int u,int fa){
	for(int k=head[u];k;k=fro[k]){
		int x=to[k];
		if(x==fa)continue;
		dfs(x,u);
	}
	add(root[u],1,n,u);
	siz[u]++;
//	cout<<u<<":"<<tr[root[u]].sum<<" "<<(1ll*n*(n-1)/2-tr[root[u]].sum+mod)%mod<<endl;
	if(u!=1)ans=(ans+(1ll*n*(n-1)/2-tr[root[u]].sum)+mod)%mod;
	if(siz[fa]>siz[u])merge(root[fa],root[u],1,n);
	else merge(root[u],root[fa],1,n),root[fa]=root[u];
	siz[fa]+=siz[u];
	return ;
}
int fastpow(int x,int y){
	int ansn=1;
	while(y){
		if(y&1)ansn=1ll*ansn*x%mod;
		x=1ll*x*x%mod;
		y/=2;
	}
	return ansn;
}
signed main(){
	n=rd();
	for(int i=1;i<n;i++){
		int x=rd(),y=rd();
		addline(x,y),addline(y,x);
	}
	dfs(1,0);
	ans=ans*2%mod;
	printf("%d",1ll*ans*fastpow(1ll*n*(n+1)/2%mod,mod-2)%mod);
	return 0;
}
posted @   flywatre  阅读(31)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
点击右上角即可分享
微信分享提示