2019-10-15

T1

这道题就是一个小模拟,甚至一个细节都没有(一定要打感叹号!!!!!)

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
char ch[30];
int sd[110][15][15],t,cnt;
int x,y,z;
int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0' || ch>'9')
	{
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9')
	{
		x=x*10+ch-'0';
		ch=getchar();
	}
	return x*f;
}

inline void add(int x,int y,int z,int kind)
{
	for(int i=1;i<=9;i++)
		for(int j=1;j<=9;j++)
			sd[kind][i][j]=sd[kind-1][i][j];
	if(sd[kind-1][x][y])
	{
		printf("Error!
");
		return ;
	}
	for(int i=1;i<=9;i++)
	{
		if(sd[kind-1][x][i]==z)
		{
			printf("Error:row!
");
			return ;
		}
	}
	for(int i=1;i<=9;i++)
	{
		if(sd[kind][i][y]==z)
		{
			printf("Error:column!
");
			return ;
		}
	}
	for(int i=(((x+2)/3)-1)*3+1;i<=(((x+2)/3)-1)*3+3;i++)
	{
		for(int j=(((y+2)/3)-1)*3+1;j<=(((y+2)/3)-1)*3+3;j++)
		{
			if(sd[kind][i][j]==z)
			{
				printf("Error:square!
");    // 这里一定要打感叹号
				return ;
			}
		}
	}
	sd[kind][x][y]=z;
	printf("OK!
");
}

inline void del(int x,int y,int kind)
{
	for(int i=1;i<=9;i++)
		for(int j=1;j<=9;j++)
			sd[kind][i][j]=sd[kind-1][i][j];
	if(!sd[kind][x][y]){
		printf("Error!
");
		return ;
	}
	printf("OK!
");
	sd[kind][x][y]=0;
}
inline void query(int x,int y,int kind)
{
	for(int i=1;i<=9;i++)
		for(int j=1;j<=9;j++)
			sd[kind][i][j]=sd[kind-1][i][j];
	if(sd[kind][x][y]){
		printf("Error!
");
		return ;
	}
	int ans[10],sum=0;
	for(int i=1;i<=9;i++)	ans[i]=0;
	for(int i=1;i<=9;i++)
		if(sd[kind][x][i]) ans[sd[kind][x][i]]=1;
	for(int i=1;i<=9;i++)
		if(sd[kind][i][y]) ans[sd[kind][i][y]]=1;
	for(int i=(((x+2)/3)-1)*3+1;i<=(((x+2)/3)-1)*3+3;i++)
		for(int j=(((y+2)/3)-1)*3+1;j<=(((y+2)/3)-1)*3+3;j++)
			if(sd[kind][i][j])
				ans[sd[kind][i][j]]=1;
	for(int i=1;i<=9;i++)
		if(!ans[i]) sum++;
	printf("%d
",sum);
	for(int i=1;i<=9;i++)
		if(!ans[i])	printf("%d
",i);
		
}

bool check(int num,int x,int y,int kind)
{
	for(int i=1;i<=9;i++)
		if(sd[kind][x][i]==num)
		{
			return false;
		}
	for(int i=1;i<=9;i++)
		if(sd[kind][i][y]==num)
			return false;
	for(int i=(((x+2)/3)-1)*3+1;i<=(((x+2)/3)-1)*3+3;i++)
		for(int j=(((y+2)/3)-1)*3+1;j<=(((y+2)/3)-1)*3+3;j++)
		{
			if(sd[kind][i][j]==num)
				return false;
		}
	return true;
}

inline void merge(int x,int y,int kind)
{
	int ans1=0,ans2=0;
	for(int i=1;i<=9;i++)
	{
		for(int j=1;j<=9;j++)
		{
			if(sd[x][i][j] && check(sd[x][i][j],i,j,kind))
			{
				sd[kind][i][j]=sd[x][i][j];
				ans1++;
				continue;
			}
			else if(sd[y][i][j] && check(sd[y][i][j],i,j,kind))
			{
				sd[kind][i][j]=sd[y][i][j];
				ans2++;
				continue;
			}
			else{
				sd[kind][i][j]=0;
				continue;
			}
		}
	}
	printf("%d %d
",ans1,ans2);
}

inline void print(int kind)
{
	for(int i=1;i<=9;i++)
		for(int j=1;j<=9;j++)
			sd[kind][i][j]=sd[kind-1][i][j];
	printf("+-+-+-+-+-+-+-+-+-+
");
	for(int i=1;i<=9;i++)
	{
		for(int j=1;j<=9;j++)
		{
			printf("|%d",sd[kind][i][j]);		
		}
		printf("|
");
		printf("+-+-+-+-+-+-+-+-+-+
");
	}
	return ;
}

int main()
{
	freopen("sudoku.in","r",stdin);
	freopen("sudoku.out","w",stdout);
	scanf("%s",ch);
	for(int i=1;i<=9;i++)
	{
		scanf("%s",ch+1);
		for(int j=1;j<=9;j++)
			sd[0][i][j]=ch[j*2]-'0';
		scanf("%s",ch+1);
	}
	t=read();
	for(int i=1;i<=t;i++)
	{
		scanf("%s",ch+1);
		if(ch[1]=='I')
		{
			x=read();y=read();z=read();
			add(x,y,z,++cnt);
		}
		else if(ch[1]=='D')
		{
			x=read();y=read();
			del(x,y,++cnt);
		}
		else if(ch[1]=='Q')
		{
			x=read();y=read();
			query(x,y,++cnt);
		}
		else if(ch[1]=='M')
		{
			x=read();y=read();
			merge(x,y,++cnt);
		}
		else if(ch[1]=='P')
		{
			print(++cnt);
		}
	}
	return 0;
}

没打感叹号,优秀而精彩的拿了70分

T2

首先在做题的时候,一看到希望最大值最小,我就觉得是一个二分答案。但是二分了答案之后,没有办法验证答案是否合法。所以这道题就不是一个二分答案(并不是所有求最大值最小,最小值最大的题都是二分答案)。 然后我们分析这道题,是否可以用相邻交换排序来进行贪心
我们先来试一下:令(i,j)(i=j-1) ,显然当前的两个大臣对前面的大臣没有影响,对后面的大臣有影响,因此我们应该使得这两个大臣交换位置或者没交换位置处在后面的大臣拿到的金币更少
设之前所有的(a)值相加为(pre),前面一个大臣的(c)(cxk)
因此不交换金币后面的大臣拿到的金币为(max(pre+a_i+b_i+b_j,cxk+b_i+b_j,pre+a_i+a_j+b_j)),交换后拿到的金币为(max(pre+a_j+b_j+b_i,cxk+b_j+b_i,pre+a_j+a_i+b_i))
若此时前者大于后者,就应该交换大臣顺序
其中(,cxk+b_i+b_j)=(cxk+b_j+b_i) 因此只需要比较
(max(pre+a_i+b_i+b_j,pre+a_i+a_j+b_j)和max(pre+a_j+b_j+b_i,pre+a_j+a_i+b_i)) 同时用(pre+a_i+a_j+b_i+b_j)减去,再经过玄学的变换之后可以得到当(min(a_j,b_i)<min(a_i,b_j))时需要交换 前后大臣
那么当(min(a_j,b_i)=min(a_i,b_j))的时候应该怎么排序呢?按照(STL)的sort的话,如果这样子,那么大臣是不会交换顺序的 但是这样做到底对不对呢? 显然是不对的。再序列不满足严格弱序的情况下,有可能会出现前面的元素比后面的元素大
再结合这道题分析,发现(a)的前缀对答案是有影响的,因此再(min(a_j,b_i)=min(a_i,b_j))的情况下,应该使得(a)更小的在前面
代码如下:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring> 
#define int long long 
using namespace std;
const int maxn=50010;
int sum,ans;
int t,n;
struct node{
	int a;int b;
	inline bool operator <(const node &x)const 
	{
		return (min(a,x.b)==min(b,x.a) && a<x.a) || min(a,x.b)<min(b,x.a);
	}
}cxk[maxn];
int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0' || ch>'9')
	{
		if(ch=='-')
			f=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9')
	{
		x=x*10+ch-'0';
		ch=getchar();
	}
	return x*f;
}

signed main()
{
	t=read();
	while(t--)
	{
		n=read();
		for(int i=1;i<=n;i++)
		{
			cxk[i].a=read();cxk[i].b=read();
		}
		sort(cxk+1,cxk+1+n);
		ans=sum=0;
        for (int i=1;i<=n;++i)
        {
            sum+=cxk[i].a;
            ans=max(ans,sum)+cxk[i].b;
        }
        printf("%lld
",ans);
	}
	return 0;
}

T3

这道题是一道关于期望的题。学长说,一般来说把期望和二进制搞在一起考,多半就是要把二进制每一位拆开。。然后这道题就没了吗?不!这道题很难
先讨论当B机器不成功的情况下: 显然在二进制下的每一位都是独立的,因此我们可以在(O(logn))的复杂度内算出第i位为1和0的数分别有(x和(n-x)),那么这一位对答案的贡献为(2^i*(x/n)*((n-x)/n)*(1-p)*2)
现在我们来看怎么在(O(logn))的复杂度内算出
用temp表示前面对后面的影响
首先 若当前位为0:对后面没有影响,因为你这一位在前面全部选满的情况下 你只能选0,对后面一点用都没有
否则 当前位为1:f表示当前位能选1的情况
那么f=temp+前面取满,当前位取1,(即相当于包括当前位前面全部取慢)后面所能取到的情况数量
我们现在再来更深入的理解temp 若当前位为1 那么对于后面来说,如果后面位要取1在计算时当前位可以为0,这时当前位并没有取慢,后面位想怎么取就怎么取(因为当前位处于高位,如果当前位没取满的话,后面可以随便取的,那么情况数量可以直接计算,因为后面不管怎么取都不会超,增加的数量相当于后面减去一位的(0,1)全排列) 而若当前位为0 对于后面来说 当前位只能取0,而且取0是取满了的,对后面有限制条件,不能现在就计算了

然后讨论B机器成功的情况下:
分析:每一个数它的异或出来的最大值肯定是最高位到最低位全部为1
那么先假设全部都满足这种情况
所以令(ans2=(db)(n+1.)*(bin[bit]-1))
因为 若当前位为0 若需要保证最大异或 那么相当于我们需要当前位为1的数 因此我们需要计算当前位为1取不到的情况,显然为前面排满,当前位取1,后面位取满的情况数量 即为后面位的(0,1)全排列
显然 temp表示的是前面排满,后面的全排列
值得注意的是 若当前位为1 那么在计算后面的时候若当前位为0,后面的全排列全是合法的,因此(temp>>=1)

#include <cstdio>
#define db double
#define int long long 
int f,bin[61],n,temp;
db ans1,ans2,p;
int digit[61],bit,i,j;
signed main(){
  scanf("%lld%lf",&n,&p),bin[0]=1,temp=--n;//我的代码需要的是n-1
  while(temp)digit[++bit]=temp&1,temp>>=1,bin[bit]=bin[bit-1]<<1;
  for(i=bit;i>0;--i){
    f=temp;
    //f表示所到达不了的数量 
    if(digit[i])
    {
    	f+=(n&(bin[i-1]-1))+1;
    	temp+=bin[i-2];//到达1的数量应该增加 (n&(bin[i-1]-1))+1个 除了正在计算的一位取1以外,其他位均可以取0 所以temp应该增加后面除了一位的0,1全排列
    }
    ans1+=(db)f*(n-f+1)*2.*bin[i-1];
  }
  ans1=ans1/(n+1.0)/(n+1.0);
  ans2=(db)(n+1.0)*(bin[bit]-1);//因为每个数开始的时候 最大可能的异或值均为bin[bit]-1
  //所以 我们将ans2赋初值为 极限情况 即每个数都能到达其最大异或值 
  temp=bin[bit]; 
  for(i=bit;i>0;--i)
    if(digit[i]&1)
		temp>>=1; //这里temp为什么要右移1 可以看前面
    else 
		ans2-=(db)(temp>>1)*bin[i-1];//当前为0 若要保证最大异或 则我们需要减去当前取不到1的情况之和*bin[i-1] 
		//因为当前为0 枚举后面时 这个只能为0且为边界状况 对后面没有贡献 
  ans2/=(n+1.0);//然后就完了 
  ans1=ans1*(1.0-p);
  ans2=ans2*p;
  ans1=ans1+ans2;
  int wei=0;
	while(ans1>=10.0)
	{
		wei++;
		ans1/=10.0;
	}
  printf("%.5f %d
",ans1,wei);
  return 0;  
}
原文地址:https://www.cnblogs.com/mendessy/p/11687627.html