目录
Codeforces 1114
貌似最近平均难度最低的一场div2了...
但我没有把握住机会TAT
D题没往DP想 写模拟自闭了40多分钟...才发现是个傻逼区间DP
再多二十分钟就能调出F的傻逼错误了...
A.Got Any Grapes?
puts
的返回值原来是0。。刚开始写的return !puts("NO");
在样例上RE了一次= =
#include <set> #include <map> #include <cstdio> #include <cctype> #include <vector> #include <cstring> #include <algorithm> #define pc putchar #define gc() getchar() typedef long long LL; inline int read() { int Now=0,f=1;register char c=gc(); for(;!isdigit(c);c=='-'&&(f=-1),c=gc()); for(;isdigit(c);Now=Now*10+c-48,c=gc()); return Now*f; } int main() { int x=read(),y=read(),z=read(),a=read(),b=read(),c=read(); if(a<x) return puts("NO"),0; a-=x; if(a+b<y) return puts("NO"),0; int tot=a+b+c-y-z; if(tot<0) return puts("NO"),0; return puts("YES"),0; return 0; }
B.Yet Another Array Partitioning Task
因为可以每隔\(m\)个分一段,所以选出最大的\(m*k\)个数,每隔\(m\)个分一段就行了。
#include <set> #include <map> #include <cstdio> #include <cctype> #include <vector> #include <cstring> #include <algorithm> #define pc putchar #define gc() getchar() typedef long long LL; const int N=2e5+5; struct Node { int v,p; bool operator <(const Node &x)const { return v==x.v?p<x.p:v>x.v; } }A[N]; inline int read() { int Now=0,c=gc()); return Now*f; } int main() { static int Ans[N]; static bool vis[N]; int n=read(),m=read(),K=read(); for(int i=1; i<=n; ++i) A[i]=(Node){read(),i}; std::sort(A+1,A+1+n); LL sum=0; for(int i=1; i<=K*m; ++i) vis[A[i].p]=1,sum+=A[i].v; int cnt=0; for(int i=1,t=0; i<=n; ++i) if(vis[i] && ++t==m) t=0,Ans[++cnt]=i; printf("%I64d\n",sum); for(int i=1; i<cnt; ++i) printf("%d ",Ans[i]); return 0; }
C.Trailing loves (or L‘oeufs?)
\(Description\)
给定\(n,b\),求\(b\)进制下\(n!\)的末尾零的个数。
\(n\leq10^{18},\ b\leq10^{12}\)。
\(Solution\)
经典问题。。所以也是一道能搜到的题。。
如果\(d=10\),就是求\(n!\)有多少个\(2\)和\(5\)的因子\(c_2,c_5\),答案是\(\min\{c_2,c_5\}\),不难理解。
如果\(d\neq10\),同样对\(d\)质因数分解,令\(d=\prod_{i=1}^kp_i^{a_i},\ a_i\neq0\),依次求出\(n!\)中有多少个\(p_i\),记为\(c_i\),答案就是\(\min_{i=1}^k\{c_i\}\)。
求\(n!\)中质因子\(p\)的个数的公式:\[f(n)=\left\lfloor\frac{n}{p}\right\rfloor +\left\lfloor\frac{n}{p^2}\right\rfloor +\left\lfloor\frac{n}{p^3}\right\rfloor +\cdots\]
实际写的时候\(n\)每次除以\(p\)即可,不需要分母不断乘\(p\),会爆long long
。。。
#include <set> #include <map> #include <cstdio> #include <cctype> #include <vector> #include <cstring> #include <algorithm> #define pc putchar #define gc() getchar() typedef long long LL; const int N=1e6+5; inline LL read() { LL Now=0;register char c=gc(); for(;!isdigit(c);c=gc()); for(;isdigit(c);Now=Now*10+c-48,c=gc()); return Now; } LL Calc(LL x,LL y) { LL res=0; for(; x; x/=y) res+=x/y; return res; } int main() { static LL P[N]; static int tm[N]; LL n=read(),b=read(); //1 LL ans=1ll<<60; for(int i=2; 1ll*i*i<=b; ++i) if(!(b%i)) { LL cnt=1; b/=i; while(!(b%i)) b/=i,++cnt; ans=std::min(ans,Calc(n,i)/cnt); } if(b!=1) ans=std::min(ans,b)); printf("%I64d\n",ans); return 0; //2 int t=0; for(int i=2; 1ll*i*i<=b; ++i) if(!(b%i)) { P[++t]=i,b/=i,tm[t]=1; while(!(b%i)) b/=i,++tm[t]; } if(b!=1) P[++t]=b,tm[t]=1; ans=1ll<<60; for(int i=1; i<=t; ++i) { LL cnt=0; // for(LL x=P[i]; x<=n&&x>0; x*=P[i]) cnt+=n/x;//这么写可能直接爆longlong爆成正数啊。。。= = for(LL x=n; x; x/=P[i]) cnt+=x/P[i]; ans=std::min(ans,cnt/tm[i]); } printf("%I64d\n",ans); return 0; }
D.Flood Fill(区间DP)
刚开始一看\(O(n^2)\),就想枚举起点然后模拟。。(mdzz)
无脑区间DP。。
\(f[i][j][0/1]\)表示合并完\(i\sim j\)区间,现在颜色是\(c_i/c_j\)的最小花费。转移特判一下即可。
或者先去掉相邻的重复元素,直接\(f[i][j]\)表示合并完\(i\sim j\)区间的最小花费。如果\(c_i=c_j\),\(f[i][j]=f[i+1][j-1]+1\);否则\(f[i][j]=\min(f[i+1][j],\ f[i][j-1])+1\)。
#include <set> #include <map> #include <cstdio> #include <cctype> #include <vector> #include <cstring> #include <algorithm> #define pc putchar #define gc() getchar() typedef long long LL; const int N=5005; int A[N],f[N][N][2]; inline int read() { int Now=0,c=gc()); return Now*f; } bool Check(int n) { for(int i=1; i<=n; ++i) if(A[i]!=A[1]) return 0; puts("0"); return 1; } int main() { int n=read(); for(int i=1; i<=n; ++i) A[i]=read(); if(Check(n)) return 0; memset(f,0x3f,sizeof f); for(int i=1; i<=n; ++i) f[i][i][0]=f[i][i][1]=0; for(int l=1; l<n; ++l) for(int i=1; i+l<=n; ++i) { int j=i+l; f[i][j][0]=std::min(f[i][j-1][0]+2-2*(A[i]==A[j]),std::min(f[i][j-1][1]+2-(A[j-1]==A[j])-(A[i]==A[j]),std::min(f[i+1][j][0]+1-(A[i]==A[i+1]),f[i+1][j][1]+1-(A[j]==A[i])))); f[i][j][1]=std::min(f[i][j-1][0]+1-(A[i]==A[j]),std::min(f[i][j-1][1]+1-(A[j-1]==A[j]),std::min(f[i+1][j][0]+2-(A[i]==A[i+1])-(A[i]==A[j]),f[i+1][j][1]+2-2*(A[j]==A[i])))); } printf("%d\n",std::min(f[1][n][0],f[1][n][1])); return 0; }
E.Arithmetic Progression(交互 二分 随机化)
首先通过操作二可以二分出最大值。
然后剩下\(30\)次操作一能干什么呢。。只能帮我们确定出数列中有某些数,那能做的好像就是随机确定\(30\)个数。
等差数列中任意两个数作差可以得到\(x\cdot d\),把确定出的数两两作差然后对差求\(\gcd\),是有可能得到真正的\(d\)的。
正确概率是多少呢,感觉挺高的。。然后有最大值有公差,就做完了。
官方题解中对正确概率有证明,大约是\(1.86185\timES10^{-9}\)。
做差的时候先排序,只求出相邻两个数的差就够了,并不需要所有数两两之间作差。
#include <ctime> #include <cstdio> #include <cctype> #include <algorithm> #define gc() getchar() #define Flush() fflush(stdout) typedef long long LL; const int N=1e6+5; int id[N]; inline int read() { int Now=0;register char c=gc(); for(;!isdigit(c);c=gc()); for(;isdigit(c);Now=Now*10+c-48,c=gc()); return Now; } inline int Rd() { return rand()<<15|rand(); } inline int Query1(int n) { int p=Rd()%n+1; printf("? %d\n",id[p]),Flush(); std::swap(id[p],id[n]);//不这么写也行 差别不大 无所谓啦 return read(); } inline int Query2(int x) { printf("> %d\n",x),Flush(); return read(); } int main() { static int A[66]; srand(time(0)); int n=read(); int l=0,r=1e9,mid,mx=0,rest=60; while(l<=r) if(--rest,Query2(mid=l+r>>1)) mx=l=mid+1; else r=mid-1; for(int i=1; i<=n; ++i) id[i]=i; int t=0; rest=std::min(rest,n); for(int Now=n; rest; --rest) A[++t]=Query1(Now--); std::sort(A+1,A+1+t),A[++t]=mx; int d=A[2]-A[1]; for(int i=3; i<=t; ++i) if(A[i]!=A[i-1]) d=std::__gcd(d,A[i]-A[i-1]); printf("! %d %d\n",mx-(n-1)*d,d),Flush(); return 0; }