男人八题2019
打的第三年男人八题了= = 感觉自己可能能创造一个EZ历史上打过最多男人八题的人(嘤嘤嘤我明明是妹子啊
考场上是5题 目前补了7题
Biology
模拟= =
//Love and Freedom. #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #define ll long long #define inf 20021225 using namespace std; int read() { int s=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();} while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return f*s; } int main() { int k=read(); int ans=0,n=0; while(++n && k) { ans++; k--; if(!k) break; if(n!=1) ans+=n, k--; } printf("%d\n",ans); return 0; }
Chemistry
发现最有用的是中间的 它在每一个小矩形都算了 所以它对矩形之间的差是没有贡献的 然后考虑我们把两个矩形补齐 所以就是看最小的和和最大的和然后直接乘一下就可以了
//Love and Freedom. #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #define ll long long #define inf 20021225 using namespace std; int read() { int s=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();} while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return f*s; } int main() { //freopen("chemistry.in","r",stdin); //freopen("chemistry.out","w",stdout); int n=read(),a=read(),b=read(),c=read(),d=read(); int s1=a+b,s2=a+c,s3=b+d,s4=c+d; int mn=min(s1,min(s2,min(s3,s4))),mx=max(s1,max(s2,max(s3,s4))); int k=mx-mn; k=n-k; printf("%I64d\n",1ll*k*n); return 0; }
Chinese
考虑dp $f[i][j]=\sum_{k=0}^{i-1} f[i-1][j-k]$ 显然前缀和优化 O(nk)
//Love and Freedom. #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #define ll long long #define inf 20021225 #define mdn 998244353 #define N 1001 using namespace std; int read() { int s=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();} while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return f*s; } void upd(int &x,int y){x+=x+y>=mdn?y-mdn:y;} int f[N],pre[N]; int qry(int l,int r) { if(l<=0) return pre[r]; return (mdn+pre[r]-pre[l-1])%mdn; } int main() { int n=read(),k=read(); f[0]=pre[0]=1; for(int i=1;i<=n;i++) { int top=min(1ll*k,1ll*i*(i-1)/2); for(int j=1;j<=top;j++) pre[j]=pre[j-1],upd(pre[j],f[j]); for(int j=0;j<=top;j++) f[j]=qry(j-i+1,j); } printf("%d\n",f[k]); return 0; }
English
其实之前看过但是考场上死活想不起来怎么做= = 二分答案然后考虑怎么判断 中位数常见套路<x记为0 >=x记为1 然后发现最靠近mid的连续两个0/1就是答案 特判01交替 多的最后会留下来 复杂度O(nlgn)
//Love and Freedom. #include<algorithm> #include<cstring> #include<cmath> #include<cstdio> #define ll long long #define inf 20021225 #define N 100010 using namespace std; int read() { int s=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();} while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return f*s; } int a[N<<1],tmp[N<<1],n; bool check(int x) { for(int i=1;i<(n<<1);i++) tmp[i]=a[i]>=x; int mid=n; for(int d=0;d<n-1;d++) { if(tmp[mid-d-1]==tmp[mid-d]) return tmp[mid-d]; if(tmp[mid+d+1]==tmp[mid+d]) return tmp[mid+d]; } return tmp[1]; } int main() { n=read(); int mx=1; for(int i=1;i<(n<<1);i++) a[i]=read(),mx=max(mx,a[i]); int l=1,r=mx,ans=1; while(l<=r) { int mid=l+r>>1; if(check(mid)) l=mid+1,ans=mid; else r=mid-1; } printf("%d\n",ans); return 0; }
Geography
2017男人八题原题海星 直接反过来做然后线段树/bit维护就行
//Love and Freedom. #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #define ll long long #define inf 20021225 #define N 100010 #define lowbit(x) (x&-x) using namespace std; int read() { int s=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();} while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return f*s; } struct bit { int a[N],n; void add(int x){while(x<=n) a[x]++,x+=lowbit(x);} int qry(int x){int ans=0; while(x) ans+=a[x],x-=lowbit(x); return ans;} }t[2]; int cnt; void modify(int l,int r) { t[0].add(l); t[1].add(r); cnt++; } int query(int l,int r) { int ans1=t[0].qry(r); ans1=cnt-ans1; int ans2=t[1].qry(l-1); return cnt-ans1-ans2; } int main() { int n=read(); t[0].n=t[1].n=n; while(n--) { int t=read(),l=read(),r=read(); if(t==1) modify(l,r); else printf("%d\n",query(l,r)); } return 0; }
History
考虑直接暴力维护前k小值 然后呢有一个指针指向当前k个里面的最大的记为x 然后选进来一个看它>x的话直接选走 不然的话就选走x把它扔进来 然后发现这玩意是单调的所以它只会减O(n)次 那么我们就做完了
//Love and Freedom. #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #define ll long long #define inf 20021225 #define N 100010 using namespace std; int read() { int s=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();} while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return f*s; } int a[N],buc[N],n,k,p,tmp[N]; ll val[2]; int main() { n=read(); for(int i=1;i<=n;i++) a[i]=read(); sort(tmp+1,tmp+n+1); k=read(); while(k--) { p=read(); int k=1,top=0; val[0]=val[1]=0; for(int i=1;i<p;i++) buc[a[i]]++,top=max(top,a[i]); for(int i=p;i<=n;i++) { if(a[i]>top) val[k]+=a[i], k^=1; else { val[k]+=top; buc[top]--; k^=1; buc[a[i]]++; while(!buc[top] && top) top--; } } for(;top;top--) while(buc[top]) val[k]+=top,k^=1,buc[top]--; printf("%lld\n",val[1]-val[0]); } return 0; }
Math
留坑
Physics
好题ovo 做过弱化版 是正睿17的给 有一个N^2神仙DP方法之前会现在不会了反正对加强版不重要我们就不管了
考虑暴力DP $f[i][j]$表示左走不超过i一共j个叶子 $f[i][j]= [j==1] + \sum_{k=0}^j f[i-1][k] * f[i][j-k]$ 典型的卷积形式 根据常见套路 我们把它写作生成函数形式$F_i(x)$
有$F_i(x)=x+F_i(x)*F_{i-1}(x)$ 所以有$F_i(x)=\frac{x}{1-F_{i-1}(x)}$ 继续根据常见套路 可以把$F_i(x)$写作$\frac{A_i(x)}{B_i(x)}$ 然后我们发现这个式子性质很好 把$F_{i-1}(x)$用这玩意替换一下可以得到递推关系
$A_i(x)=xB_{i-1}(x)$ $B_i(x)=B_{i-1}(x)-A_{i-1}(x)$ 这个玩意显然可以用矩乘优化转移 具体形式懒得写了可以看代码 有边界$A_0(x)=x$,$B_0(x)=1$ 还是常见套路 我们把单位根带进去 然后IDFT一下就可以分别得到A和B接着多项式求逆一下就做完了qwq
代码也不长很好写。
//Love and Freedom. #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #define ll long long #define inf 20021225 #define mdn 998244353 #define N 400010 #define G 3 using namespace std; int read() { int s=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();} while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+ch-'0',ch=getchar(); return f*s; } void upd(int &x,int y){x+=x+y>=mdn?y-mdn:y;} struct mtx { int a[2][2],n; mtx(){memset(a,0,sizeof(a)); n=2;} void diag(){memset(a,0,sizeof(a)); for(int i=0;i<n;i++) a[i][i]=1;} }; mtx operator*(mtx a,mtx b) { mtx tmp=mtx(); for(int i=0;i<a.n;i++) for(int j=0;j<a.n;j++) for(int k=0;k<a.n;k++) upd(tmp.a[i][j],1ll*a.a[i][k]*b.a[k][j]%mdn); return tmp; } mtx mksm(mtx bs,int mi) { mtx ans=mtx(); ans.diag(); while(mi) { if(mi&1) ans=ans*bs; bs=bs*bs; mi>>=1; } return ans; } int ksm(int bs,int mi) { int ans=1; while(mi) { if(mi&1) ans=1ll*ans*bs%mdn; bs=1ll*bs*bs%mdn; mi>>=1; } return ans; } int r[N]; int init(int n) { int l=0,lim=1; while(lim<n) lim<<=1,l++; for(int i=0;i<lim;i++) r[i]=(r[i>>1]>>1)|((i&1)<<l-1); return lim; } void ntt(int *a,int lim,int f) { for(int i=0;i<lim;i++) if(r[i]>i) swap(a[r[i]],a[i]); for(int k=2,mid=1;k<=lim;k<<=1,mid<<=1) { int Wn=ksm(G,(mdn-1)/k); if(f) Wn=ksm(Wn,mdn-2); for(int w=1,i=0;i<lim;i+=k,w=1) for(int j=0;j<mid;j++,w=1ll*w*Wn%mdn) { int x=a[i+j],y=1ll*w*a[i+mid+j]%mdn; a[i+j]=(x+y)%mdn; a[i+mid+j]=(mdn+x-y)%mdn; } } if(f) for(int inv=ksm(lim,mdn-2),i=0;i<lim;i++) a[i]=1ll*a[i]*inv%mdn; } int tmp[N]; void poly_inv(int *a,int *ans,int n) { if(n==1){ans[0]=ksm(a[0],mdn-2); return;} int mid=n+1>>1; poly_inv(a,ans,mid); int lim=init(n<<1); for(int i=0;i<n;i++) tmp[i]=a[i]; for(int i=n;i<lim;i++) tmp[i]=0; ntt(tmp,lim,0); ntt(ans,lim,0); for(int i=0;i<lim;i++) ans[i]=(2ll-1ll*tmp[i]*ans[i]%mdn+mdn)*ans[i]%mdn; ntt(ans,lim,1); for(int i=n;i<lim;i++) ans[i]=0; } mtx solve(int w,int n) { mtx k; k.n=2; k.a[0][0]=1; k.a[0][1]=mdn-1; k.a[1][0]=w; k.a[1][1]=0; k=mksm(k,n); mtx ans; ans.a[0][0]=(k.a[0][0]+1ll*w*k.a[0][1]%mdn)%mdn; ans.a[1][0]=(k.a[1][0]+1ll*w*k.a[1][1]%mdn)%mdn; return ans; } int a[N],b[N],f[N],ib[N]; int main() { int n=read(),m=read(); int lim; mtx ans; lim=init(n+2); int Wn=ksm(G,(mdn-1)/lim); for(int i=0,w=1;i<lim;i++,w=1ll*w*Wn%mdn) ans=solve(w,n),a[i]=ans.a[1][0],b[i]=ans.a[0][0],printf("%d %d\n",a[i],b[i]); ntt(a,lim,1); ntt(b,lim,1); poly_inv(b,ib,m); lim=init(m+n+2); ntt(ib,lim,0); ntt(a,lim,0); for(int i=0;i<lim;i++) f[i]=1ll*a[i]*ib[i]%mdn; ntt(f,lim,1); for(int i=1;i<=m;i++) printf("%d\n",f[i]); return 0; }