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!\n"); return ; } for(int i=1;i<=9;i++) { if(sd[kind-1][x][i]==z) { printf("Error:row!\n"); return ; } } for(int i=1;i<=9;i++) { if(sd[kind][i][y]==z) { printf("Error:column!\n"); 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!\n"); // 这里一定要打感叹号 return ; } } } sd[kind][x][y]=z; printf("OK!\n"); } 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!\n"); return ; } printf("OK!\n"); 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!\n"); 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\n",sum); for(int i=1;i<=9;i++) if(!ans[i]) printf("%d\n",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\n",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("+-+-+-+-+-+-+-+-+-+\n"); for(int i=1;i<=9;i++) { for(int j=1;j<=9;j++) { printf("|%d",sd[kind][i][j]); } printf("|\n"); printf("+-+-+-+-+-+-+-+-+-+\n"); } 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\n",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:其所有1的情况就是前面对后面的影响数量
否则 用f表示当前位对后面影响数量
那么f=temp+前面取满,当前位取1,后面所能取到的
然后讨论B机器成功的情况下:
分析:每一个数它的异或出来的最大值肯定是最高位到最低位全部为1
那么先假设全部都满足这种情况
所以令$ ans2=(db)(n+1.)*(bin[bit]-1)$
因为 若当前位为0 那么相当于我们需要当前位为1的数 显然我们需要知道在前面的影响下 如何才能让当前位为1合法 所以只能是前面位没有取满 当前位取1 后面位任意的情况才可以,而我们更方便求的是前面位对后面位对后面位不行的情况
因此令temp表示前面位对后面不行的情况
#include <cstdio> typedef double db; typedef long long LL; LL f,bin[61],n,temp; db ans1,ans2,p; int digit[61],bit,i,j; int 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;//之前位占满的情况下当前位到达不了的数量应该增加 (n&(bin[i-1]-1))+1个 temp+=bin[i-2];//这一位占满了 那么后面位不能到达的就会多一些 //因为当前位如果为1或0 均可以使后面位成立 //否则(当前位为0) 若是后面位为0 那么当前位只能为0 没有贡献 //即相当于当前位为0 后面必选0 没有贡献 //这一位如果是1的话,对后面都有贡献,所以要累加temp } ans1+=(db)f*(n-f+1)*2.*bin[i-1]; } ans1=ans1/(n+1.)/(n+1.); ans2=(db)(n+1.)*(bin[bit]-1);//因为每个数开始的时候 最大可能的异或值均为bin[bit]-1 //所以 我们将ans2赋初值为 极限情况 即每个数都能到达其最大异或值 temp=bin[bit]; //表示前面占满 当前若为0取不到的(即相当于当前要取1所能取到的 for(i=bit;i>0;--i) if(digit[i]&1) temp>>=1;//若当前为1 相当于在后面取得时候 前面为0时必定取得到后面所有的情况 //因此 不行的情况要减少 else ans2-=(db)(temp>>1)*bin[i-1];//当前为0 减掉不能取到的 并且 当前对后面没有贡献 //因为当前为0 枚举后面时 这个只能为0且为边界状况 对后面没有贡献 ans2/=(n+1.);//然后就完了 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\n",ans1,wei); return 0; }