其他分享
首页 > 其他分享> > 首师大附中集训第四天综合测试

首师大附中集训第四天综合测试

作者:互联网

综合测试

      考试的前一天老师说今天的题比NOI稍微简单一些

      第一题,给你n,要你求f(1)\ xor\ f(2)\ xor...xor\ f(n)。定义f(i)为其所有约数的异或和。n<=10^{14}.

      这题还是比较简单的,直接数论分块,讨论那些[\frac{n}{l}]为奇数就行了。

      求前缀和的时候考虑相邻两个数(偶奇)的异或和为1.然后讨论一下情况就可以了。

#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;

long long n;
long long ans=0;

long long get_sum(long long x){
	if(x%2==1) return ((x+1)/2)%2;
	else return x^((x/2)%2);
}

int main(){
	//freopen("Xor.in","r",stdin);
	//freopen("Xor.out","w",stdout);
	scanf("%lld",&n);
	long long l=1,r;
	while(l<=n){
		r=n/(n/l);
		if((n/l)&1){
			if(l==r) ans^=l;
			else ans^=get_sum(r)^get_sum(l-1);
		}
		l=r+1;
	}
	printf("%lld",ans);
}

      第二题:给定长度为n的序列,每次给出一个[l,r]和一个x。

      对于每一个询问计算有多少个三元组满足l<=i<j<k<=r\& a_i|a_j|a_k=x

      这题我的做法貌似和其他人不太一样,我是直接考虑补集转化。然后把或操作就变成了与操作。

      接着,对于每一个ai,将他的子集位置上的值加上1,开个前缀数组记录一下,那么一个位置上的值就记录了所有超集的个数。

      对于每一次询问的x,我们考虑一个三元组与的值不为x但在x的位置上被计算的,直接枚举x的超集,容斥一下就可以了。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;

int n,m;
int p[100010][256];
int a[100010];

long long C(long long x){
	if(x<=2) return 0;
	return 1ll*x*(x-1)*(x-2)/6;
}

long long dfs(int l,int r,int x,int now,int coef){
	if(now==8) {
		return C(p[r][x]-p[l-1][x])*coef;
	}
	long long tot=0;
	tot+=dfs(l,r,x,now+1,coef);
	if(!(x&(1<<now))) tot+=dfs(l,r,x|(1<<now),now+1,-coef);
	return tot;
}

int main(){
	//freopen("Or.in","r",stdin);
	//freopen("Or.out","w",stdout);
	scanf("%d %d",&n,&m);
	int l,r,x;
	for(int i=1;i<=n;i++)  {
		scanf("%d",&x);x^=255;
		for(int j=0;j<=255;j++) {
			p[i][j]=p[i-1][j];
			if((x|j)==x) p[i][j]++;
		}
	}
	while(m--){
		scanf("%d %d %d",&l,&r,&x);x^=255;
		printf("%lld\n",dfs(l,r,x,0,1));
	}
}

      第三题:从这题往后就没有正解了。

      给出一张图,问存不存在两棵生成树没有重边。n<=10,m<=100

      我的做法是直接暴力枚举边是否选(满足第一颗生成树的条件),然后在这个过程中check一下是否存在第二颗生成树。如果不存在就直接返回,因为到后面状态数就越多,所以这个剪枝在前面做就可以了,拿到了非比寻常的55分!

       然而,有人直接每一次做很多次random_shuffle。然后从前往后做两遍最小生成树,就拿到了满分(我补题的时候写了这个东西连样例都过不了

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

int T;
int n,m;
struct edge{
	int x,y;
}s[110];
int fa[11],size[11],f[11];
bool tf[110],we=false;
int data=4;

int fp(int x){
	if(f[x]!=x) return f[x]=fp(f[x]);
	return x;
}

int findpa(int x){
	if(fa[x]!=x) return findpa(fa[x]);
	return x;
}

bool check(){
	for(int i=1;i<=n;i++) f[i]=i;
	int tot=0;
	for(int i=1;i<=m;i++) if(!tf[i]){
		int fx=fp(s[i].x),fy=fp(s[i].y);
		if(fx!=fy) f[fx]=fy,tot++;
		if(tot==n-1) return true;
	}
	return false;
}

bool dfs(int x,int now){
	if(now==n && check()) return true;
	if(x==m+1 || now==n) return false;
	int fx=findpa(s[x].x),fy=findpa(s[x].y);
	if(dfs(x+1,now)) return true;
	if(fx!=fy){
		if(size[fx]<size[fy]) swap(fx,fy);
		fa[fy]=fx;size[fx]+=size[fy];tf[x]=true;
		if((now>data || check()) && dfs(x+1,now+1)) return true;
		size[fx]-=size[fy],fa[fy]=fy;tf[x]=false;
	}
	return false;
}

int main(){
	//freopen("Game.in","r",stdin);
	//freopen("Game.out","w",stdout);
	scanf("%d",&T);
	while(T--){
		scanf("%d %d",&n,&m);
		for(int i=1;i<=n;i++) fa[i]=i,size[i]=1;
		for(int i=1;i<=m;i++) scanf("%d %d",&s[i].x,&s[i].y),tf[i]=false;
		printf(dfs(1,1)?"Possible\n":"Impossible\n");
	}
}

      第四题:啊这题真的亏,YY了一个决策单调性,结果最后5分钟hack了自己。

      有两种二元组(x,y),对于两个二元组要你计算(x2-x1)(y2-y1)。其中1必须属于第一种二元组,2必须属于第二种二元组。

      而且要求x1<=x2。问最大值。

      我只会暴力。

      听别人讲:把xy画在二维平面上,然后知道答案就相当于一个矩形的面积。

      接着我们看一下第一类点,知道只有类似下凸壳上面的点才是有用的(这个下凸壳只要求x递增,y递减)。

      第二类点,知道只有类似上凸壳上面的点才是有用的。(同理)

      接着我们把两个序列random_shuffle5次,每次取前2000个点出来算一下点对。就可以拿到95的高分!

      不用暴力,把相邻两个点的交界线算出来,然后相邻两条线算一下交点,就可以知道点在哪一块时可以获得最大价值,用set维护,可以拿到103的高分!

标签:师大附中,return,int,综合测试,long,fa,freopen,第四天,include
来源: https://blog.csdn.net/Deep_Kevin/article/details/97677199