A. Ascending Photo
贪心增广。
#include<bits/stdc++.h> using namespace std; const int MAXN = 1000000 + 10; vector<int> g[MAXN]; int a[MAXN], b[MAXN], sz[MAXN], cnt[MAXN]; bool mg[MAXN], vis[MAXN]; int n, m; bool dfs(int u, int f = -1) { if (g[u].empty()) return false; for (auto &p: g[u]) if (p != f) { if (!vis[p + 1] || cnt[u + 1] == 1) { mg[p] = vis[p] = vis[p + 1] = 1; if (f != -1) mg[f] = 0; return true; } } for (auto &p: g[u]) if (p != f) { if (dfs(u + 1, p + 1)) { mg[p] = vis[p] = vis[p + 1] = 1; if (f != -1) mg[f] = 0; return true; } } return false; } int main(){ scanf("%d", &n); m = 0; for (int i = 0, p(-1); i < n; ++ i) { int x; scanf("%d", &x); if (x == p) sz[m - 1] ++; else { p = x; a[m] = x; sz[m ++] = 1; } } n=m; for(int i=0;i<n;i++)b[i]=a[i]; sort(b,b+n); for(int i=m=0;i<n;i++)if(i==0||b[i]>b[m-1])b[m++]=b[i]; for (int i = 0; i < n; ++ i) { a[i] = lower_bound(b,b+m,a[i])-b; cnt[a[i]] ++; } for (int i = 0; i + 1 < n; ++ i) { if (a[i] + 1 == a[i + 1]) g[a[i]].push_back(i); } int ret = n; for (int i = m - 1; i >= 0; -- i) { if (dfs(i)) -- ret; } printf("%d", ret); }
B. Boss Battle
当$nleq 3$时显然$1$步就可以炸死。否则每次可以缩小一格,故答案为$n-2$。
#include<cstdio> int n,ans; int main(){ scanf("%d",&n); if(n<=3)ans=1; else ans=n-2; printf("%d",ans); }
C. Connect the Dots
留坑。
D. Dunglish
按题意模拟即可。
#include<cstdio> #include<iostream> #include<map> #include<string> using namespace std; int n,m,i; string a[100]; long long tot,co; map<string,int>f,g; map<string,string>o; int main(){ cin>>n; for(i=1;i<=n;i++){ cin>>a[i]; } cin>>m; while(m--){ string a,b,c; cin>>a>>b>>c; o[a]=b; if(c[0]=='c')f[a]++;else g[a]++; } tot=1; co=1; for(i=1;i<=n;i++){ tot*=f[a[i]]+g[a[i]]; co*=f[a[i]]; } if(tot==1){ for(i=1;i<=n;i++)cout<<o[a[i]]<<" "<<endl; if(co==1){ printf("correct"); }else{ printf("incorrect"); } }else{ printf("%lld correct ",co); printf("%lld incorrect ",tot-co); } }
E. English Restaurant
在最后新添$n$个容量$+infty$的桌子,表示离开餐馆,然后将桌子按容量排序。
因为$期望=frac{总和}{方案数}$,所以可以同时DP出总和以及方案数。
注意到最终占据的一定是若干个区间,首先预处理出$w[i][j]$表示只有$[i,j]$桌子被占据的总和以及方案数,枚举最后一张桌子转移。
然后设$f[i][j]$表示$[i,n]$这些人占据了$[j,t]$这些桌子的总和以及方案数,利用前缀和优化转移。
转移时需要用组合数体现顺序,时间复杂度$O(n^3)$。
#include<cstdio> #include<algorithm> using namespace std; const int N=210; int n,g,t,i,j,k,x,y,c[N];double C[N][N]; struct P{ double u,d; P(){} P(double _u,double _d){u=_u,d=_d;} P operator+(const P&b){return P(u*b.d+d*b.u,d*b.d);} P operator*(const P&b){return P(u+b.u,d+b.d);} void operator+=(const P&b){*this=*this+b;} void operator*=(const P&b){*this=*this*b;} }w[N][N],f[N][N],s[N][N],tmp; inline P cal(int l,int r){ int x=min(g,l>0?c[l-1]:0),y=min(g,c[r]); return P(c[r]<N?(y*(y+1)-x*(x+1))/2:0,y-x); } int main(){ scanf("%d%d%d",&n,&g,&t); for(i=0;i<n;i++)scanf("%d",&c[i]); sort(c,c+n); for(i=0;i<t;i++)c[n++]=N; for(C[0][0]=1,i=1;i<n+5;i++)for(C[i][0]=1,j=1;j<=i;j++)C[i][j]=C[i-1][j-1]+C[i-1][j]; for(j=0;j<n;j++){ w[j][j]=cal(j,j); for(i=j-1;~i;i--)for(k=i;k<=j;k++){ tmp=P(0,C[j-i][k-i])+cal(i,k); if(i<k)tmp+=w[i][k-1]; if(j>k)tmp+=w[k+1][j]; w[i][j]*=tmp; } } for(i=t-1;~i;i--)for(j=n-1;~j;j--)if(t-i<=n-j)f[i][j]=w[j][j+t-i-1]; for(i=t-1;~i;i--)for(j=n-1;~j;j--){ for(x=i+1;x<t;x++)f[i][j]*=P(0,C[t-i][t-x])+w[j][j+x-i-1]+s[x][j+x-i+1]; s[i][j]=s[i][j+1]*f[i][j]; } tmp=P(0,0); for(i=0;i<n;i++)tmp*=f[0][i]; return printf("%.15f",tmp.u/tmp.d),0; }
F. Factor-Free Tree
限制条件等价于每个点和子树内每个点都互质,这说明在中序遍历上往前往后若干个互质。
分解质因数,维护每个质因子最后一次出现的位置,即可求出$[l_i,r_i]$表示$i$与往前往后多少范围内都互质。
按中序遍历从左往右依次考虑每个点,用一个栈维护之前部分的树的最右链,对于$i$,先将它往下挂,然后尝试往上浮动,因为$r$越大的点越靠近根更优,除非$l$太大以至于不能覆盖住左边的子树。
一旦一个点的父亲确定,那么它的超过父亲的$r$的部分是毫无用处的,将$r$直接和父亲取$min$。
如此一来,每条链上满足$r$递减,故直接暴力退栈直到找到合适的位置即可。
构造部分时间复杂度为$O(n)$。
#include<stdio.h> #include<string.h> #include<string> #include<ctype.h> #include<math.h> #include<set> #include<map> #include<vector> #include<queue> #include<bitset> #include<algorithm> #include<time.h> using namespace std; void fre() { } #define MS(x, y) memset(x, y, sizeof(x)) #define ls o<<1 #define rs o<<1|1 typedef long long LL; typedef unsigned long long UL; typedef unsigned int UI; template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; } template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; } const int N = 1000010, M = 10000010, Z = 1e9 + 7, inf = 0x3f3f3f3f; template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; } int casenum, casei; int n,i,j; int v[M],p[N],tot,last[M]; int a[N],left[N],right[N]; struct S { int l, r, rpow; }s[N]; int fa[N]; int main() { for(i=2;i<M;i++){ if(!v[i]){ p[tot++]=i; v[i]=i; } for(j=0;j<tot&&i*p[j]<M;j++){ v[i*p[j]]=p[j]; if(i%p[j]==0)break; } } while(~scanf("%d",&n)) { for(i=1;i<=n;i++)scanf("%d",&a[i]); for(i=2;i<M;i++)last[i]=0; for(i=1;i<=n;i++){ left[i]=0; int x=a[i]; while(x>1){ //printf("! %d %d ",i,v[x]); left[i]=max(left[i],last[v[x]]); x/=v[x]; } left[i]++; x=a[i]; while(x>1){ last[v[x]]=i; x/=v[x]; } } for(i=2;i<M;i++)last[i]=n+1; for(i=n;i;i--){ right[i]=n+1; int x=a[i]; while(x>1){ right[i]=min(right[i],last[v[x]]); x/=v[x]; } right[i]--; x=a[i]; while(x>1){ last[v[x]]=i; x/=v[x]; } } //for(i=1;i<=n;i++)printf("%d [%d,%d] ",i,left[i],right[i]); int top = 0; s[0].rpow = inf; bool flag = 1; for(int i = 1; i <= n; ++i) { fa[i] = 0; s[top + 1].r = 0; int lft = i; while(left[i] <= s[top].l && s[top].rpow <= right[i]) { gmin(lft, s[top].l); --top; } fa[s[top + 1].r] = i; fa[i] = s[top].r; ++top; s[top].l = lft; s[top].r = i; s[top].rpow = min(right[i], s[top - 1].rpow); //printf("i: %d l = %d r = %d rpow = %d ", i, s[top].l, s[top].r, s[top].rpow); if(s[top - 1].rpow < i) { flag = 0; //printf("i = %d s[top - 1].rpow = %d ", i, s[top - 1].rpow); } } if(!flag)puts("impossible"); else { for(int i = 1; i <= n; ++i)printf("%d ", fa[i]); puts(""); } } return 0; } /* 【trick&&吐槽】 【题意】 【分析】 【时间复杂度&&优化】 */
G. Glyph Recognition
枚举形状,然后二分求出半径。
#include<stdio.h> #include<iostream> #include<string.h> #include<string> #include<ctype.h> #include<math.h> #include<set> #include<map> #include<vector> #include<queue> #include<bitset> #include<algorithm> #include<time.h> using namespace std; void fre() { } #define MS(x, y) memset(x, y, sizeof(x)) #define ls o<<1 #define rs o<<1|1 typedef long long LL; typedef unsigned long long UL; typedef unsigned int UI; template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; } template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; } const int N = 1010, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f; template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; } int casenum, casei; const double eps = 1e-8; inline int sgn(double x) {return fabs(x) < eps ? 0 : (x < 0 ? -1 : 1);} inline double sqr(double x){return x * x;} struct point { double x, y; point(){} point(double a, double b) : x(a), y(b) {} friend point operator + (const point &a, const point &b){ return point(a.x + b.x, a.y + b.y); } friend point operator - (const point &a, const point &b){ return point(a.x - b.x, a.y - b.y); } friend point operator * (const point &a, const double &b){ return point(a.x * b, a.y * b); } friend point operator * (const double &a, const point &b){ return point(a * b.x, a * b.y); } friend point operator / (const point &a, const double &b){ return point(a.x / b, a.y / b); } double norm(){ return sqrt(sqr(x) + sqr(y)); } } ; double det(const point &a, const point &b) { return a.x * b.y - a.y * b.x; } double dot(const point &a, const point &b) { return a.x * b.x + a.y * b.y; } double dist(const point &a, const point &b) { return (a - b).norm(); } point rotate_point(const point &p, double A) { double tx = p.x, ty = p.y; return point(tx * cos(A) - ty * sin(A), tx * sin(A) + ty * cos(A)); } bool PointOnSegment(point p, point s, point t) { return sgn(det(p - s, t - s)) == 0 && sgn(dot(p - s, p - t)) <= 0; } struct polygon { int n; point a[N]; polygon(){} double area(){ double sum = 0; a[n] = a[0]; for(int i = 0; i < n; i ++) sum += det(a[i + 1], a[i]); return sum / 2; } int Point_In(point t){ int num = 0, i, d1, d2, k; a[n] = a[0]; for(i = 0; i < n; i ++){ if(PointOnSegment(t, a[i], a[i + 1])) return 2; k = sgn(det(a[i + 1] - a[i], t - a[i])); d1 = sgn(a[i].y - t.y); d2 = sgn(a[i + 1].y - t.y); if(k > 0 && d1 <= 0 && d2 > 0) num ++; if(k < 0 && d2 <= 0 && d1 > 0) num --; } return num != 0; } }c; point p[N]; const double PI = acos(-1.0); void make(int n, double r) { double ang = PI * 2.0 / n; c.a[0] = point(r, 0); for(int i = 1; i < n; i ++){ c.a[i] = rotate_point(c.a[0], PI * 2 - ang * i); } } int n; bool check() { for(int i = 0; i < n; i ++){ if(c.Point_In(p[i]) == 0) return 0; } return 1; } bool check2() { for(int i = 0; i < n; i ++){ if(c.Point_In(p[i]) == 1) return 0; } return 1; } const double INF = 1e9; int main() { scanf("%d", &n); for(int i = 0; i < n; i ++){ scanf("%lf%lf", &p[i].x, &p[i].y); } double k = 0; int ans; for(int i = 3; i <= 8; i ++){ c.n = i; double l = 0, r = INF; for(int tim = 1; tim <= 1000; tim ++){ double mid = (l + r) / 2; make(i, mid); if(check()) r = mid; else l = mid; } double out = c.area(); l = 0, r = INF; for(int tim = 1; tim <= 1000; tim ++){ double mid = (l + r) / 2; make(i, mid); if(check2()) l = mid; else r = mid; } double in = c.area(); double tmp = in / out; if(tmp > k){ k = tmp; ans = i; } } printf("%d %.10f ", ans, k); } /* 【trick&&吐槽】 【题意】 【分析】 【时间复杂度&&优化】 */
H. High Score
因为平方增长较快,所以全部给某个数是最优的,但在小数据下不一定成立,故小数据暴力枚举即可。
#include<iostream> #include<algorithm> using namespace std; typedef long long ll; const int K=1000; int T;ll a,b,c,d,i,j,k,ans; inline ll cal(ll a,ll b,ll c){return a*a+b*b+c*c+min(a,min(b,c))*7;} int main(){ cin>>T; while(T--){ cin>>a>>b>>c>>d; ans=0; for(i=0;i<=d&&i<=K;i++)for(j=0;i+j<=d&&j<=K;j++){ k=d-i-j; ans=max(ans,cal(a+i,b+j,c+k)); ans=max(ans,cal(a+i,b+k,c+j)); ans=max(ans,cal(a+k,b+i,c+j)); } ans=max(ans,cal(a+d,b,c)); ans=max(ans,cal(a,b+d,c)); ans=max(ans,cal(a,b,c+d)); cout<<ans<<endl; } }
I. Installing Apps
每个应用可以看成会先占用$max(d,s)$的空间,然后释放$max(d,s)-s$的空间。
假设全部应用都要安装,那么按照释放空间的大小从大到小安装最优。
如此排序之后设$f[i][j]$表示考虑前$i$个应用,剩余空间为$j$时最多安装几个应用即可。
时间复杂度$O(nc)$。
#include<cstdio> #include<algorithm> using namespace std; const int N=505,M=10010; int n,m,i,j,x,f[N][M],g[N][M],w[N][M],q[N],cnt; struct P{int p,w,t;}a[N]; inline bool cmp(const P&a,const P&b){return a.t>b.t;} int main(){ scanf("%d%d",&n,&m); for(i=1;i<=n;i++){ int d,s; scanf("%d%d",&d,&s); a[i].p=i; a[i].w=max(d,s); a[i].t=a[i].w-s; } sort(a+1,a+n+1,cmp); for(i=0;i<=n;i++)for(j=0;j<=m;j++)f[i][j]=-M; f[0][m]=0; for(i=1;i<=n;i++){ for(j=0;j<=m;j++){ f[i][j]=f[i-1][j]; g[i][j]=j; w[i][j]=0; } for(j=0;j<=m;j++)if(f[i-1][j]>=0){ if(j<a[i].w)continue; if(f[i-1][j]+1>f[i][j-a[i].w+a[i].t]){ f[i][j-a[i].w+a[i].t]=f[i-1][j]+1; g[i][j-a[i].w+a[i].t]=j; w[i][j-a[i].w+a[i].t]=a[i].p; } } } for(i=x=0;i<=m;i++)if(f[n][i]>f[n][x])x=i; printf("%d ",f[n][x]); for(i=n;i;i--){ if(w[i][x])q[++cnt]=w[i][x]; x=g[i][x]; } for(i=cnt;i;i--)printf("%d ",q[i]); }
J. Juggling Troupe
对于$i$处的$2$,往前往后找到第一个$0$的位置$l,r$,将$l$和$r$赋值为$1$,然后将$l+r-i$赋值为$0$即可。
set维护,时间复杂度$O(nlog n)$。
#include<cstdio> #include<set> #include<cstring> using namespace std; const int N=1000010; set<int>T;int n,i,x;char a[N]; int main(){ scanf("%s",a+1); n=strlen(a+1); T.insert(0); T.insert(n+1); for(i=1;i<=n;i++)if(a[i]=='0')T.insert(i); for(i=1;i<=n;i++)if(a[i]=='2'){ set<int>::iterator r=T.lower_bound(i),l=r; l--; x=*l+*r-i; if(*l>0)T.erase(l); if(*r<=n)T.erase(r); T.insert(x); } for(i=1;i<=n;i++)a[i]='1'; for(set<int>::iterator it=T.begin();it!=T.end();it++)a[*it]='0'; for(i=1;i<=n;i++)putchar(a[i]); }
K. Knockout Tournament
对于$1$,要让他的对手尽量弱小,而对于其他人,要让他的对手和他水平尽量接近,以降低他的胜率。
故将$1$看作$0$水平后从小到大排序,相邻的配对即可。
然后计算概率的时候只需要暴力枚举两边的胜者,两个点只会在lca处被计算,故时间复杂度为$O(n^2)$。
#include<cstdio> #include<algorithm> #include<vector> using namespace std; typedef pair<int,double>P; const int N=8200; int n,m,i,j,a[N],start; vector<P>f[N]; inline double win(int x,int y){ return 1.0*a[x]/(a[x]+a[y]); } inline void cal(int x){ int l=x<<1,r=x<<1|1; for(int _=0;_<2;_++){ for(vector<P>::iterator i=f[l].begin();i!=f[l].end();i++){ int A=i->first; double B=0; for(vector<P>::iterator j=f[r].begin();j!=f[r].end();j++){ B+=win(A,j->first)*j->second; } f[x].push_back(P(A,B*i->second)); } swap(l,r); } } int id[N],cnt; void dfs(int x){ if((x<<1)>m){ id[++cnt]=x; //printf("%d %d ",cnt,x); } if((x<<1)<=m)dfs(x<<1); if((x<<1|1)<=m)dfs(x<<1|1); } int main(){ scanf("%d",&n); for(i=1;i<=n;i++){ scanf("%d",&a[i]); } a[0]=a[1]; a[1]=0; sort(a+1,a+n+1); a[1]=a[0]; m=n*2-1; start=m-n+1; dfs(1); for(i=1;i<=n;i++){ int x=id[n-i+1]; f[x].push_back(P(i,1)); } for(i=start-1;i;i--){ cal(i); } for(i=0;i<f[1].size();i++)if(f[1][i].first==1){ printf("%.10f",f[1][i].second); } }