zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    a[::-1]相当于 a[-1:-len(a)-1:-1],也就是从最后一个元素到第一个元素复制一遍。
    +=
    map 和reduce
    赋值语句
    高阶函数
    函数式编程
    迭代器
    如何判断一个对象是可迭代对象呢?方法是通过collections模块的Iterable类型判断:
    ie11升级的过程中遇到的问题以及解决办法
    .csporj 文件部分节点解析
  • 原文地址:https://www.cnblogs.com/Zmonarch/p/13921996.html
Copyright © 2011-2022 走看看