11.3号 模拟赛

总分 40分
T1: 0分
T2: 0分
T3: 20分
T4: 20分

对于T1

题意:

  Aliemo 有两个数 a,b ,但是他想考考你,所以他想给你另外两个数 x,y。

a+b 的值为 x ,a&b 的值为 y,首先需要判断能否有一组 a,b 满足当前的情况,如果有,那么求出
a xor b,否则输出 −1 (其中 a,b > 0 )

输入:

  第一行为一个正整数 T,表示组数。
  接下来 T 行,每一行有两个整数 x,y。

输出:

  对于每一组数据,按题意输出 a xor b 或者 −1

解题思路:

对于两个数,a,b;

  异或可以看做不进位加法,a&b也就是只有在都是一的时候才是1

a+b=((a&b)<<1) + a^b;

所以,答案就是x-2*y, &的时候取得是相同的位,而a+b取得是不同的位,所以在进行判别的时候先将 a+b 和 a&b 这个 &一下,如果&为零,那就是真,否则就是是假

考试暴力代码(期望得分 30分,实际得分 0分):

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#define int long long
using namespace std;

inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){ if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)){ x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int a,b;
signed main()
{
	int t=read();
	while(t--)
	{
		int flag=0;
		a=read(),b=read();
		int pos1=-1,pos2=-1;
		for(int i=0;i<=a;i++)
		{
			for(int j=0;j<=a;j++)
			{
				if(i+j==a && (i&j)==b)
				{
					flag=1;
					pos1=i;
					pos2=j;	
					break;
				}
			}
			if(flag) break;
		}
		if(flag) cout<<(pos1^pos2)<<endl;
		else cout<<"-1"<<endl; 
	}
	return 0;
}

根据讲完之后代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<ctime>
#include<cstdlib>
using namespace std;
long long int read() 
{
  long long int s = 0, f = 0; char ch = getchar();
  while (!isdigit(ch)) f |= (ch == '-'), ch = getchar();
  while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar();
  return f ? -s : s;
}
int main()
{
	int n;
	n=read();
	for(int i=1;i<=n;i++)
	{
		long long int x,y;
		x=read();
		y=read();
		if(x<y)
		{
			cout<<-1<<endl;
			continue;
		}
		long long int ans1=min(x,y);
		long long int ans2=max(x,y)-min(x,y);
		if((ans1&ans2)==y) 
		{
			cout<<abs(ans2-ans1)<<endl;	//x-2*y
			continue;
		}
		else cout<<-1<<endl;	
	}
	return 0;
} 

T2 :游戏

题目:

  luckyblock 又开始和社团的萌妹子玩游戏了。
  在今天的游戏中,luckyblock 将会得到一个 n × m 且全为小写字母的矩阵,他可以从矩阵中任选
  一块正方形,但必须保证该正方形中任意一类小写字母个数之和不能超过 k,换而言之,在该正方形
  中, ‘a’字符个数不能超过 k , ‘b’字符个数不能超过 k,..., ‘z’字符个数不能超过 k。
  luckyblock 现在想知道,以 (i,j) 为左上角且符合以上要求的正方形中,边长最大的是多少?

输入:

  第一行三个正整数 n,m,k,
  接下来 n 行,每行 m 个小写字母。

输出:

  输出 n 行,每行 m 个数字。其中第 i 行第 j 个数字表示,以 (i,j) 为左上角且符合题目要求的正
  方形的最大边长。

解题思路:

O((n^2))的枚举每一个点,然后二分一下边长,利用二维前缀和进行字符的判别

在进行二分的时候,注意边界

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){ if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)){ x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int map[302][302][26];
int sum[302][302][27];
int ans[302][302];
int k,n,m;
bool check(int l,int r,int mid)
{
	int flag=0;
	for(int i=1;i<=26;i++)
	{
		int sum_=sum[l + mid - 1][r + mid - 1][i]-sum[ l + mid - 1 ][ r - 1][ i ]- sum[ l- 1][r + mid - 1][i]+sum[ l - 1][ r - 1][i];
		if(sum_>k)
		{
			flag=1;
		}
	}
	if(flag==0) return  true;
	else return false;
}
int main()
{
	n=read(),m=read(),k=read();
	for(int i=1;i<=n;i++)//求出前缀和 
	{
		for(int j=1;j<=m;j++)
		{
			char c;
			cin>>c;
			map[i][j][c-'a'+1]=1;
			for(int k=1;k<=26;k++)
			{
				sum[i][j][k]=sum[i-1][j][k]+sum[i][j-1][k]-sum[i-1][j-1][k]+map[i][j][k];
			}
		}
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			int l=0,r=min(n-i+1,m-j+1);//边长
			 while(l<=r)
			 {
			 	int mid=l+r>>1;
			 	if(check(i,j,mid))
			 	{
			 		l=mid+1;
				}
				else 
				{
					r=mid-1;
				}
			 }
			 cout<<r<<" ";
		}
		cout<<endl;
	} 
	return 0;
}

T3:或和异或

题目:

loceaner 最近在研究位运算,它研究的主要有两个:or 和 xor。 (C 语言中对于 | 和∧�

为了更好的了解这两个运算符,loceaner 找来了一个 2 n 长度的数组。它第一次先对所有相邻两个

数执行 or 操作,得到一个 2 n−1 长度的数组。也就是说,一开始时数组为 a[1],a[2],…,a[2 n ],执行完第

一次操作后,会得到 a[1] or a[2],a[3] or a[4],…,a[2 n − 1] or a[2 n ]。

第二次操作,loceaner 会将所有相邻两个数执行 xor 操作,得到一个 2 n−2 长度的数组,假如第一

次操作后的数组是 b[1],b[2],⋯,b[2 n−1 ],那么执行完这次操作后会变成 b[1] xor b[2], b[3] xor b[4] ,⋯,

b[2 n−1 − 1] xor b[2 n−1 ]。

第三次操作,loceaner 仍然将执行 or 操作,第四次 loceaner 执行 xor 操作。如此交替进行。

最终这 2 n 个数一定会变成 1 个数。loceaner 想知道最终这个数是多少。

为了让这个游戏更好玩,loceaner 还会执行 Q 次修改操作。每次修改原先的 2 n 长度的数组中的

某一个数,对于每次修改操作,你需要输出 n 次操作后(最后一定只剩下唯一一个数)剩下的那个数

是多少。

输入:

第一行两个数 n ,Q。

接下来一行 2 n 个数 a i 表示一开始的数组。

接下来 Q 行,每行两个数 (x_i),(y_i),表示 loceaner 这次的修改操作是将 a[(x_i)] 改成 (y_i)

输出:

Q 行,表示每次修改操作后执行 n 次操作后剩下的那个数的值。�

输入样例:

  2 4
  1 6 3 5
  1 4
  3 4
  1 2
  1 2

输出样例:

  1
  3
  3
  3

样例说明:

第一次修改,4,6,3,5->6,7->1

第二次修改,4,6,4,5->6,5->3

第三次修改,2,6,4,5->6,5->3

第四次修改,2,6,4,5->6,5->3

数据范围:

对于 30% 的数据 n ≤ 17 ,Q = 1。

对于另外 20% 的数据 n ≤ 10 ,Q ≤ 1000 。

对于再另外 30% 的数据 n ≤ 12 ,Q ≤ 100000。

对于 100% 的数据 1 ≤ n ≤ 17,1 ≤ Q ≤(10^5) ,1 ≤(x_i)(2^n) ,0 ≤ (y_i)<(2^{30}) ,0 ≤(a_i)<(2^{30})

解题思路:

进行手画一次图之后,就会发现,它就是一个倒着的线段树,然后直接建树就可以了,然后对于每一次修改就是单点修改

我一开始就是修改一次建一次树,然后就起飞了,只有20分

正解代码(和我的差不了多少):

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
const int maxn=1000000;
struct node
{
	int left,right,now,sum;
};
node tree[maxn<<2];
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){ if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)){ x=x*10+ch-'0';ch=getchar();	}
	return x*f;
} 
void pushdown(int now)
{
	if(tree[now].now%2==1)
	{
		tree[now].sum=(tree[now<<1].sum|tree[now<<1|1].sum);
	}
	else 
	{ 
		tree[now].sum=(tree[now<<1].sum ^ tree[now<<1|1].sum);
	}
	return ;
}
void build(int now,int left,int right)
{
	tree[now].left=left;
	tree[now].right=right;
	if(left==right)
	{
		tree[now].sum=read();
		tree[now].now=0;
		return;
	}
	int mid=left+right>>1;
	build(now<<1,left,mid);
	build(now<<1|1,mid+1,right);
	tree[now].now=tree[now<<1].now+1;
	pushdown(now);
} 
void updata(int now,int left,int right,int val,int k)
{
	if(left==right)
	{
		tree[now].sum=val;
		tree[now].now=1;
		return ;
	}
	int mid=(left+right)>>1;
	if(k<=mid)
	{
		updata(now<<1,left,mid,val,k);
	}
	else 
	{
		updata(now<<1|1,mid+1,right,val,k);
	}
	pushdown(now);
}
int main()
{
	int n=read();
	int t=read();
	build(1,1,(1<<n));
	while(t--)
	{
		int a=read(),b=read();
		updata(1,1,(1<<n),b,a);
		//for(int i=1;i<=(1<<n);i++)
		printf("%d
",tree[1].sum);
	}
	return 0;
}

T4 链接

题目:

Kersen 来到了一片森林, 森林中有一些树和连接两棵树的无向道路, 保证这些道路能把森林连通。

Kersen 对这片森林做了一些考察,有了两个奇怪的发现:

- 森林中的树总共分为两种,不妨记为 0 型树和 1 型树。

- 这些道路的长度都是 2 的整数次幂且互不相同,第 i 条道路的长度为 2 i 。

Kersen 又发现了这片森林的一个神奇之处,任何两棵类型不同的树之间都可以构成一组链接,这

一对链接的能量值为两棵树之间的最短路。

好奇的 Kersen 想知道这片森林所有链接的能量值之和,请你来帮帮他。

输入:

输入第一行包含两个整数 n,m ,表示森林中树的数量和无向道路的数量。

接下来一行包含 n 个整数 a 1 ,a 2 ,...,a n (ai = 0or1),表示每一棵树的类型。

接下来 m 行,第 i 行表示第 i 条无向道路,包含两个整数 (u_i) ,(v_i) (1 ≤ (u_i),(v_i)≤ n) 表示第 i 条无向

道路连接的树的编号,并且它的长度为 (2^i)

输出:

输出一个整数,所有链接能量值之和对 10 9 + 7 取模的结果

输入样例:

  3 2
  0 1 0
  3 1
  1 2

输出样例

   10

解题思路:

首先,我们的数学知识帮助了我们, (sum_{2^i})是要小于 (2^i),所以第 i条边是一定比前面所有边的和要大的,所以,我们就能只能知道,两棵树 的最短路径就一定在最小生成树上,所以我们先跑一颗最小### 生成树,,然后在树上做DP,

开始DP,按照 DP的思路,我们现在已经知道 节点i的ans了,那么现在的问题就是怎么由它去更新得到to的值,我们从 x 点转移到 to 点的时候,to 以及 to 子树内的点

到 to 的距离是 到 x 距离减

去这条边的权值。 然后从 x 转移到 to 点,可以看出除了 to 及其子树中的点,边权都增加了这条边

的边权

然后状态转移方程就是 (ans_to=)(ans_x)-(size_to imes dis) +((size_x)-(size_to)) ( imes dis)

一开始我是暴力枚举所有点,跑最短路,求贡献(期望得分 20,最终得分 20分):

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#define inf 0x3f
const int maxn=20000;
const int mod=1e9+7;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){ if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)){ x=x*10+ch-'0';ch=getchar();	}
	return x*f;
} 
struct node
{
	int nxt,to,weath;
}edge[maxn<<1];
int number_edge,head[maxn];
void add_edge(int from,int to,int i)
{
	++number_edge;
	edge[number_edge].nxt=head[from];
	edge[number_edge].to=to;
	edge[number_edge].weath=(1<<(i-1));
	head[from]=number_edge;
}
int a[maxn],n,m;
int dis[maxn],vis[maxn];
void dijistra(int x)
{
	memset(dis,inf,sizeof(dis));
	memset(vis,0,sizeof(vis));
	std::priority_queue<std::pair<int,int> >q;
	dis[x]=0;
	q.push(std::make_pair(0,x));
	while(!q.empty())
	{
		int u=q.top().second;
		q.pop();
		if(vis[u]) continue;
		vis[u]=1;
		for(int i=head[u];i;i=edge[i].nxt)
		{
			int v=edge[i].to;
			if(!vis[v])
			{
				if(dis[v]>dis[u]+edge[i].weath)
				{
					dis[v]=dis[u]+edge[i].weath;
					q.push(std::make_pair(-dis[v],v));
				}
			}
		}
	}
}
int tumb[200][200];
int main()
{
	n=read(),m=read();
	for(int i=1;i<=n;i++)
	{
		a[i]=read();
	}
	for(int i=1;i<=m;i++)
	{
		int u=read(),v=read();
		add_edge(u,v,i);
		add_edge(v,u,i);
	}
	int ans=0;
	for(int i=1;i<=n;i++)
	{
		dijistra(i);
		for(int j=1;j<=n;j++)
		{
			if(tumb[i][j]==1)
			{
				continue;
			}
			else if(a[j]!=a[i])
			{
				ans+=dis[j];
			}
		}
	}
	printf("%d",ans%mod);
	return 0;
}

讲完之后打出来的,40分代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
#include <map>
#include <stack>
#define int long long
#define inf 0x3f
const int maxn=200000;
const int mod=1e9+7;
struct node
{
	int nxt,to,weath;
};
node edge[maxn<<2];
int number_edge,head[maxn<<1];
int point_to[maxn],a[maxn];
int size[maxn];
int dis[maxn];
int point;
int f[maxn<<1];
int n,m,number_point;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){ if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)){ x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
void add_edge(int from,int to,int weath)
{
	++number_edge;
	edge[number_edge].nxt=head[from];
	edge[number_edge].weath=weath%mod;
	edge[number_edge].to=to;
	head[from]=number_edge;
}
int findx(int x)
{
	while(x!=point_to[x])
	{
		x=point_to[x];
	}
	return point_to[x];
}
void dfs1(int x,int fa) 
{
	for(int i=head[x];i;i=edge[i].nxt)
	{
		int v=edge[i].to;
		if(v==fa) continue;
		dis[v]=dis[x]+edge[i].weath;
		dis[v]%=mod;
		dfs1(v,x);
		size[x]+=size[v];
	} 
}
void dfs2(int x,int fa)
{
	for(int i=head[x];i;i=edge[i].nxt)
	{
		int v=edge[i].to;
		if(v == fa) continue;
    	int cnt = (((point -size[v]))%mod+mod)%mod;
		int money=(size[v]*edge[i].weath)%mod;
    	cnt*=edge[i].weath;
    	cnt%=mod;
		f[v]=((f[x] + cnt-money)%mod+mod)%mod;
		dfs2(v,x);
	}
} 
signed main()
{
	n=read(),m=read();
	for(int i=1;i<=n;i++)
	{
		a[i]=read();
		point_to[i]=i;
	}
	int flag=(a[1] == 1);
	for(int i=1;i<=n;i++)
	{
		a[i]^=flag,size[i]=a[i];
	}
	for(int i=1;i<=m;i++)
	{
		int u=read(),v=read();
		int f_u=findx(u),f_v=findx(v);
		if(f_u!=f_v)
		{
			point_to[f_u]=f_v;
			add_edge(u,v,(1<<i));
			add_edge(v,u,(1<<i));
		}
	}
	dfs1(1,0);
	point=size[1];
	for(int i=1;i<=n;i++)
	{
		if(a[i])
		{
			f[1]+=dis[i];
			f[1]%=mod;
		}
	}
	dfs2(1,0);
	int ans=0;
	for(int i=1;i<=n;i++)
	{
		if(!a[i])
		{
			ans+=f[i];
			ans%=mod;
		}
	}
	printf("%d",ans%mod);
	return 0;
}

看了标程的代码
正解代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#define int long long
#define INF (1e13 + 7)
#define MANX MAXN
#define MAXN 200000
#define MOD 1000000007
using namespace std;
inline int read()
{
	int x = 0, f = 1; char c = getchar();
	while (c > '9' || c < '0') {if (c == '-') f = -1; c = getchar();}
	while (c >= '0' && c <= '9') {x = x * 10 + (c ^ 48); c = getchar();}
	return f * x;
}
int n, m, a[MAXN], fa[MAXN];
inline int find(int x){return fa[x] == x ? x : fa[x] = find(fa[x]);}
inline int qpow(int a, int b)
{
	int sum = 1;
	while (b)
	{
		if (b & 1) sum *= a, sum %= MOD;
		a *= a, a %= MOD, b >>= 1;
	}
	return sum;
}

struct node{
	int v, w, next;
}edge[MAXN];

int Num, head[MAXN];

inline void build(int u, int v, int w)
{
	Num++;
	edge[Num].v = v;
	edge[Num].w = w;
	edge[Num].next = head[u];
	head[u] = Num;
}

int size[MAXN], w[MAXN], sum, f[MAXN];

void dfs(int u, int fa)
{
	for (int i = head[u]; i; i = edge[i].next)
	{
		int v = edge[i].v;
		if (v == fa) continue;
		w[v] = w[u] + edge[i].w;
		w[v] %= MOD;
		dfs(v, u);
		size[u] += size[v];
	}
}

void dfs1(int u, int fa)
{
	for (int i = head[u]; i; i = edge[i].next)
	{
		int v = edge[i].v;
		if (v == fa) continue;
		int add = ((sum - size[v]) % MOD + MOD) % MOD;
		int del = size[v] * edge[i].w;
		add *= edge[i].w;
		add %= MOD;
		f[v] = ((f[u] + add - del) % MOD + MOD) % MOD;
		dfs1(v, u);
	}
}
signed main()
{
	n = read(), m = read();
	for (int i = 1; i <= n; i++) a[i] = read(), fa[i] = i;
	int flag = (a[1] == 1);
	for (int i = 1; i <= n; i++)
		a[i] ^= flag, size[i] = a[i];
	for (int i = 1; i <= m; i++)
	{
		int u = read(), v = read();
		int fu = find(u), fv = find(v);
		if (fu == fv) continue;
		fa[fu] = fv;
		build(u, v, qpow(2, i));
		build(v, u, qpow(2, i));
	}
	dfs(1, 0);
	sum = size[1];
	for (int i = 1; i <= n; i++)
		if (a[i])
			f[1] += w[i], f[1] %= MOD;
	dfs1(1, 0);
	int ans = 0;
	for (int i = 1; i <= n; i++)
		if (!a[i])
			ans += f[i], ans %= MOD;
	cout << ans % MOD;
	return 0;
}
原文地址:https://www.cnblogs.com/Zmonarch/p/13921996.html