2021. Supplementary questions for the eighth session of Niuke multi school in summer vacation

Keywords: Algorithm

  1. Question A Ares, Toilet Ares Reading comprehension questions, sign in

The title is a little difficult to understand. A number a represents that it has been written correctly. For a question, there are k opportunities to obtain the code. The length of the code obtained each time is x and the probability of error is p (fractional form). When the code length is l, the problem can be written correctly. The sum of x times K is guaranteed to be L. Write the expected value of the number of questions, and take the modulus of the prime number

Idea: because the topic has ensured that the sum of x is L, the direct calculation of probability is to write the expected value of the problem. Finally add a.

The code is as follows:

#include <bits/stdc++.h>
using namespace std;
const int M=4933;
typedef long long LL;
int qmi(int a,int b)
{
	int res=1;
	while(b)
	{
		if(b&1)res=(LL)res*a%M;
		a=(LL)a*a%M;
		b>>=1;
	}
	return res;
}
int n,m,k,a,l;
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>n>>m>>k>>a>>l;
	LL res=1;
	for(int i=1,x,y,z;i<=k;i++)
	{
		cin>>x>>y>>z;
		if(x==0)continue;// If x is 0, no code is obtained and no probability is calculated	
		y=z-y;
		res=res*y*qmi(z,M-2)%M;
	}
	cout<<(res+a)%M<<"\n";
	return 0;
}
  1. Question D Dohna Dohna Thinking, binary

Given b array and c array, b = ( b 2 . . . . . . b n ) b=(b_2......b_n) b=(b2​......bn​) c = ( c 2 . . . . . . c n ) c=(c_2......c_n) c=(c2... cn) and defined b i = a i ∣ a i − 1 b_i=a_i|a_{i-1} bi​=ai​∣ai−1​ c i = a i + a i − 1 c_i=a_i+a_{i-1} ci = ai + ai − 1 give b array and c array, and find the number of a array satisfying the condition

Idea: for the b array, it represents the or operation of the a array. For the problems related to bit operation, many think from the perspective of binary and consider the influence of each bit separately. However, there is another c array involving addition and carry, so each bit cannot be considered separately. But we have a formula a + b = ( a ∣ b ) + ( a & b ) a+b=(a|b)+(a\&b) a+b=(a ∣ b) + (A & B) in this way, we will convert the restriction of addition and or operation in the topic into operation, and then we can consider each order separately. For each bit, we can either take 0 or 1. We can set two variables. 0 means that we can't take and 1 means that we can take. The contribution to each bit is the multiplication principle.

The code is as follows:

#include <bits/stdc++.h>
using namespace std;
const int N=100010;
typedef long long LL;
int n,b[N],c[N],d[N];
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>n;
	for(int i=2;i<=n;i++)cin>>b[i];
	for(int i=2;i<=n;i++)cin>>c[i];
	for(int i=2;i<=n;i++)d[i]=c[i]-b[i];
	LL ans=1;
	for(int i=30;i>=0;i--)
	{
		int bit1=1,bit0=1;
		for(int j=2;j<=n;j++)
		{
			int nw1=0,nw0=0;
			int x=b[j]>>i&1,y=d[j]>>i&1;
			if(x&&y)nw1=bit1;
			if(x&&!y)nw1=bit0,nw0=bit1;
			if(!x&&!y)nw0=bit0; 

			bit1=nw1,bit0=nw0;
		}
		ans=ans*(bit0+bit1);
	}
	cout<<ans<<"\n";
	return 0;
}
  1. Question E Rise of Shadows Sign in
  2. Question F Robots Thinking, preprocessing, dp

General idea: given a lattice of n * m, N, m < = 500. Some points in the grid can't go. There are three kinds of robots. The first can only go down, the first can only go right, and the third can go down and right. q queries (q < = 5e5). Each query gives the type of robot, as well as the coordinates of the starting point and end point, and asks whether it can be reached.

Idea: this problem can be written in violence. The official solution is partition. Let's see how violence is written first. We see that N and m are not big, but we ask a lot, so we think of preprocessing first. We first preprocess the points that can be reached from this point. We can use bitset, 0 / 1 to indicate whether a point can be reached. But what? In this way, the space complexity is too large. If we map the point coordinates into numbers, we also have 500 * 500 numbers, that is, we need to open a bitset with 500 * 500 in two dimensions. However, think about it carefully. When we preprocess the enumeration starting point (x,y) from the back to the front, we only use two starting points (x+1,y) and (x,y+1) to update, so we don't need to open so large for the starting point. We can save all queries and roll the array to find points and update queries at the same time.

The code is as follows:

#include <bits/stdc++.h>
using namespace std;
const int N=505;
typedef pair<int,int> PII;
int n,m,q;
char g[N][N];
bitset<N*N>f[2][N];
vector<PII> qer[N*N];
bool ans[N*1000];
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>n>>m;
	for(int i=0;i<n;i++)cin>>g[i];
	cin>>q;
	for(int i=1;i<=q;i++)
	{
		int op,sx,sy,ex,ey;
		cin>>op>>sx>>sy>>ex>>ey;
		sx--,sy--,ex--,ey--;
		if(sx>ex||sy>ey)continue;
		if(op==1&&sy!=ey)continue;
		if(op==2&&sx!=ex)continue;
		qer[sx*m+sy].push_back({i,ex*m+ey});
	}
	for(int i=n-1;i>=0;i--)
	{
		for(int j=m-1;j>=0;j--)
		{
			if(g[i][j]=='1')f[i&1][j]=0;
			else 
			{
				f[i&1][j]=f[i&1^1][j]|f[i&1][j+1];
				f[i&1][j][i*m+j]=1;
			}
			for(auto &x:qer[i*m+j])ans[x.first]=f[i&1][j][x.second];
		}
	}
	for(int i=1;i<=q;i++)
	{
		if(ans[i])cout<<"yes"<<"\n";
		else cout<<"no"<<"\n";
	}
	return 0;
}

Although direct violence writing is too much, the complexity is still a little high. Using divide and conquer optimization is nearly 10 times faster.

The idea of divide and conquer is as follows: first, the first two types of robots are specially treated. For the rest, if we can walk from the starting point to the end point, we can choose any line between the starting point and the end point. In the process of walking from the starting point to the end point, we will inevitably pass through a point on the line change. Therefore, we can change the line in the behavior, process which points can go from the starting point to the line, and which points can go from the line to the end point, and then find the intersection is the legal transfer point. And we can convert mid to middle row every time. We only need to find the number of rows from the starting point to the ending point across Mid. for others, we can divide and conquer the large interval into two according to mid. The code is as follows:

#include <bits/stdc++.h>
using namespace std;
const int N=505;
struct node{
	int sx,sy,ex,ey;
	int id;
};
bitset <N> f[N][N];
int n,m,q;
char g[N][N];
bool ans[N*1000];
void dfs(vector<node> q,int l,int r)
{
	if(q.size()==0||l==r)return ;
	vector<node> L,R,T;
	int mid=l+r >> 1;
	for(auto &x:q)
	{
		if(x.ex<=mid)L.push_back(x);
		else if(x.sx>mid)R.push_back(x);
		else T.push_back(x);
	}
	dfs(L,l,mid);
	dfs(R,mid+1,r);
	for(int i=mid;i>=l;i--)
		for(int j=m-1;j>=0;j--)
		{
			if(g[i][j]=='1')
			{
				f[i][j].reset();
				continue;
			}
			if(j<m-1)f[i][j]=f[i][j+1];
			else f[i][j].reset();
			if(i<mid)f[i][j]|=f[i+1][j];
			else f[i][j][j]=1;
		}
	for(int i=mid+1;i<=r;i++)
		for(int j=0;j<m;j++)
		{
			if(g[i][j]=='1')
			{
				f[i][j].reset();
				continue;
			}
			if(j>0)f[i][j]=f[i][j-1];
			else f[i][j].reset();
			if(i>mid+1)f[i][j]|=f[i-1][j];
			else f[i][j][j]=1;
		}
	for(auto &x:T)ans[x.id]=((f[x.sx][x.sy]&f[x.ex][x.ey]).count()>0);
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>n>>m;
	for(int i=0;i<n;i++)cin>>g[i];
	cin>>q;
	vector<node> qer;
	for(int i=1;i<=q;i++)
	{
		int op,sx,sy,ex,ey;
		cin>>op>>sx>>sy>>ex>>ey;
		sx--;sy--;ex--;ey--;
		if(sx>ex||sy>ey)continue;
		if(op==1&&sy!=ey)continue;
		if(op==2&&sx!=ex)continue;
		// Special processing in the same row and column facilitates the determination of recursive end conditions
		if(sx==ex) 
		{
			while(sy<=ey&&g[sx][sy]!='1')sy++;
			ans[i]=sy>ey;
		}
		else if(sy==ey)
		{
			while(sx<=ex&&g[sx][sy]!='1')sx++;
			ans[i]=sx>ex;
		}
		else qer.push_back({sx,sy,ex,ey,i});
	}
	dfs(qer,0,n-1);
	for(int i=1;i<=q;i++)cout<<(ans[i]? "yes":"no")<<"\n";
	return 0;
}

Conclusion: for some parts with common nature, we can divide them into several small parts and divide and rule them.

  1. Question J Tree Tree game

General idea: given a tree, the node number is 1-n. There are two people, and the node number of the two people at the beginning is given. Everyone will delete the points they have passed before and the edges connected to the points. When neither of them can move, the game ends. Both of them are the number of points they have passed. Both of them want to make their own score as high as possible and the other's score as low as possible, and both go with the optimal strategy to find the difference between the final two scores (first hand score - second hand score)

Idea: assuming that the first hand is at u and the second hand is at V, there is a shortest path in u-v. once someone leaves this path from a certain point, because it is a tree, then the two people will not affect each other. Therefore, we can first record the shortest path and then preprocess the maximum score that can be taken from each point. Remember that the first hand scores s1 and the second hand scores s2. Make s1-s2 as large as possible from the perspective of the first hand and as small as possible from the perspective of the second hand. If we only consider going first, we can traverse the shortest path away from each point, calculate the value of s1-s2 at this time, and maintain the maximum value. At this time, s2 is actually the maximum score of the backhand in an interval. We can preprocess the st table in advance. Consider only the backhand. Similarly, maintain a s1-s2 minimum value. Considering both, the search is carried out in turn through confrontation. Overall time complexity O ( n l o g n ) O(nlogn) O(nlogn). The code is as follows:

#include <bits/stdc++.h>
using namespace std;
const int N=500005;
int n,s,t;
int path[N],path1[N],path2[N],len;
int st1[N][20],st2[N][20];
vector<int> G[N];
bool st[N];
int lg[N];
void init()
{
	for(int i=0;i<20;i++)
	{
		int t=1<<i;
		if(t>=N)break;
		lg[t]=i;
	}
	for(int i=1;i<N;i++)if(!lg[i])lg[i]=lg[i-1];

	for(int j=0;j<20;j++)
	{
		for(int i=1;i+(1<<j)-1<=len;i++)
		{
			if(!j)
			{
				st1[i][j]=path1[i];
				st2[i][j]=path2[i];
			}
			else 
			{
				st1[i][j]=max(st1[i][j-1],st1[i+(1<<j-1)][j-1]);
				st2[i][j]=max(st2[i][j-1],st2[i+(1<<j-1)][j-1]);
			}
		}
	}
}
int query1(int l,int r)
{
	int k=r-l+1;
	k=lg[k];
	return max(st1[l][k],st1[r-(1<<k)+1][k]);
}
int query2(int l,int r)
{
	int k=r-l+1;
	k=lg[k];
	return max(st2[l][k],st2[r-(1<<k)+1][k]);
}
bool dfs1(int u,int from,int dep)
{
	if(u==t)
	{
		len=dep;
		path[dep]=u;
		st[u]=1;
		return 1;
	}
	for(auto v:G[u])
	{
		if(v==from)continue;
		if(dfs1(v,u,dep+1))
		{
			path[dep]=u;
			st[u]=1;
			return 1;
		}
	}
	return 0;
}
int dfs2(int u,int from,int dep)
{
	int mx=dep;
	for(auto v:G[u])
	{
		if(st[v]||v==from)continue;
		mx=max(mx,dfs2(v,u,dep+1));
	}
	return mx;
}
int dfs3(int l,int r,int tp)
{
	if(!tp)
	{
		int mx=path1[l]-query2(l+1,r);
		if(l+1<r)mx=max(mx,dfs3(l+1,r,1));
		return mx;
	}
	else 
	{
		int mx=query1(l,r-1)-path2[r];
		if(l+1<r)mx=min(mx,dfs3(l,r-1,0));
		return mx;
	}
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>n>>s>>t;
	for(int i=1;i<n;i++)
	{
		int x,y;
		cin>>x>>y;
		G[x].push_back(y);
		G[y].push_back(x);
	}
	dfs1(s,0,1);//Record and mark the points on the shortest path
	for(int i=1;i<=len;i++)path[i]=dfs2(path[i],0,0); //Preprocess the maximum score that can be obtained by leaving the shortest path from a point
	for(int i=1;i<=len;i++) //Preprocess the scores that two people have obtained if they leave from a certain point
	{
		path1[i]=path[i]+i;
		path2[i]=path[i]+len-i+1;
	}
	init(); //The maximum value of preprocessing interval in st table is used to calculate the maximum score of a certain interval in O(1)
	cout<<dfs3(1,len,0)<<"\n";
	return 0;
}
  1. Yet Another Problem About Pi Computational geometry, problem, abstract transformation

A plane region is divided into an infinite number of lattices with length d and width w. find a continuous grid with length π \pi The number of long lattices that a line of π can pass through. The boundary line of the grid does not belong to any grid

Idea: let's change the topic first. If the boundary is included, we must try to pass through as many grid points as possible. When we start, we directly go through a grid point, that is, the initial number is 4. Then we will add 2 to the number every time we pass through a grid point. If we go along the edge of the grid, it is obvious that we go along the one with smaller W and D, And try to walk in a straight line. For walking obliquely, it is obviously to save the length on the edge of the mapped grid. However, there is an exception. Although the diagonal of the square consumes length, the number of grid points reached by walking a diagonal can be + 3, which may make the answer better. Note t=min(w,d). t ∗ x + w 2 + d 2 ∗ y < = π t*x+\sqrt{w^2+d^2}*y<=\pi t∗x+w2+d2 * y < = π what we ask is 2 ∗ x + 3 ∗ y 2*x+3*y Maximum value of 2 * x+3 * y.

Let w < = d change the original formula to a ∗ x + b ∗ y < = π a*x+b*y<=\pi a∗x+b∗y<=π a = w , b > = 2 w a=w, b>=\sqrt{2}w a=w,b>=2 w when b=1.5w, there is 3a=2b. If Y > = 2, we can remove y-2 and X + 3 to make the result unchanged. When b > 1.5w, for Y > = 2, y-2 must increase x by > 3, which makes 2x+3y larger. When B < 1.5w, the same reasoning shows that x > = 3 can make the result better through transformation. Therefore, the optimal solution must satisfy the enumeration of x < 3 or Y < 2. The title says that the boundary does not count, but π \pi π is an infinite decimal, so we always have a length that can jump across the boundary repeatedly, so the boundary is OK

The code is as follows:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
double pi=acos(-1); 
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	int _;
	cin>>_;
	while(_--)
	{
		double w,d;
		cin>>w>>d;
		if(w>d)swap(w,d);
		double dg=sqrt(w*w+d*d);
		LL ans=0;
		for(int i=0;i<=2;i++)
		{
			if(i*w<=pi)ans=max(ans,(LL)(4+i*2+floor((pi-i*w)/dg)*3));//Enumerate lines
			if(i*dg<=pi)ans=max(ans,(LL)(4+i*3+floor((pi-i*dg)/w)*2));//Enumeration diagonal
		}
		cout<<ans<<"\n";
	}
	return 0;
}

The following are the questions that need to be filled in the replay of the Hunan provincial competition, which are added to this together

A row of boxes Thinking, linked list

There is a row of boxes, numbered 1, 2, 3,..., n from left to right. You can execute four commands:

  • 1 X Y means to move box X to the left of box y (if X is already on the left of Y, this instruction is ignored).
  • 2 X Y means to move box X to the right of box y (if X is already on the right of Y, this instruction is ignored).
  • 3 X Y indicates the position of exchange boxes X and Y.
  • 4 means reversing the whole chain.

The instruction is guaranteed to be legal, i.e. X is not equal to Y. For example, when n=6, after 1 1 4 is executed in the initial state, the box sequence is 2 3 1 4 5 6. Next, execute 2 3 5, and the box sequence becomes 2 1 4 5 3 6. Then execute 3 1 6 to get 2 6 4 5 3 1. Finally, execute 4 and get 1 3 5 4 6 2.

Idea: we notice that there is only the operation of exchanging positions in the title, not the operation of deleting and inserting, so we can use the idea of linked list to simulate the linked list and record the adjacent numbers on the left and right sides of each number. In this way, each time we exchange positions, it is equivalent to directly exchanging relevant pointers. For operation 4, we do not need to actually operate. Turning once is equivalent to changing the traversal direction once, and turning twice changes back. We only need to record how many times we have turned. Note that if we flip once, because we don't actually flip, operation 1 is actually equivalent to operation 2. The same is true for operation 2. In addition, for operation 3, X and y, the pointers changed when they are adjacent and not adjacent are different. Just simulate it. See code for details:

#include <bits/stdc++.h>
using namespace std;
const int N=100010;
typedef long long LL;
int n,m;
int L[N],R[N];
void lk(int x,int y)
{
	R[x]=y;
	L[y]=x;
}
int main()
{
	int cs=1;
	while(scanf("%d%d",&n,&m)!=EOF)
	{
		for(int i=1;i<=n;i++) //Change the head and tail of the team to 0
		{
			L[i]=i-1;
			R[i]=(i+1)%(n+1);
		}
		R[0]=1,L[0]=n;
		int op,x,y,to=0;
		while(m--)
		{
			scanf("%d",&op);
			if(op==4)
			{
				to^=1;
				continue;
			}
			scanf("%d%d",&x,&y);
			if(op==3&&R[y]==x)swap(x,y);// To save lx and so on, so first exchange X and Y and write lx,
			int lx=L[x],rx=R[x],ly=L[y],ry=R[y];
			if(op!=3&&to)op=3-op;
			if(op==1)
			{
				if(x==ly)continue;
				lk(lx,rx);lk(ly,x);lk(x,y);
			}
			if(op==2)
			{
				if(x==ry)continue;
				lk(lx,rx);lk(y,x);lk(x,ry);
			}
			if(op==3)
			{
				if(rx==y)
				{
					lk(lx,y);lk(y,x);lk(x,ry);
				}
				else
				{
					lk(lx,y);lk(y,rx);lk(ly,x);lk(x,ry);
				}
			}
		}
		LL ans=0;
		if(to==0)for(int i=R[0],cnt=1;cnt<=(n+1)/2;cnt++,i=R[R[i]])ans+=i;
		else for(int i=L[0],cnt=1;cnt<=(n+1)/2;cnt++,i=L[L[i]])ans+=i;
		printf("Case %d: %lld\n",cs++,ans);
	}
	return 0;
}

Posted by EGNJohn on Wed, 08 Sep 2021 14:34:15 -0700