====== 2020.07.23codeforces加训 ====== ===== 比赛情况 ===== ^题号 ^A ^B ^C ^D ^E ^F ^G ^H ^I ^J ^K | ^状态 |O |- |- |- |- |O |O |! |O |O |O | //O 在比赛中通过 Ø 赛后通过 ! 尝试了但是失败了 - 没有尝试// **比赛时间** 2020-07-23 12:00-17:00 ===== 题解 ===== ==== F - Empty Vessels ==== 可以把最大的桶作为中介构造目标,之后的东西就是搜索并记录路径就好了。 #include using namespace std; typedef long long ll; const int N = 2e4+5; int a[15],maxi; bool vis[N]; int from[N]; queueQ; void print(int x,int dep) { if (x==0) { printf("%d\n",dep); return ; } if (a[from[x]]<=x) { print(x-a[from[x]],dep+2); printf("1 %d\n3 %d %d\n",from[x],from[x],maxi); } else { print(x-a[from[x]]+a[maxi],dep+4); printf("1 %d\n3 %d %d\n2 %d\n3 %d %d\n",from[x],from[x],maxi,maxi,from[x],maxi); } } int main() { int n,A; scanf("%d%d",&n,&A); maxi = 1; for (int i = 1;i<= n;i++) { scanf("%d",&a[i]); if (a[i]>a[maxi])maxi = i; } vis[0] = true; Q.push(0); while (!Q.empty()) { int x = Q.front(); Q.pop(); for (int i = 1;i<= n;i++) if (!vis[(x+a[i])%a[maxi]]) { vis[(x+a[i])%a[maxi]] = true; from[(x+a[i])%a[maxi]] = i; Q.push((x+a[i])%a[maxi]); } } if (!vis[A]) { if (A==a[maxi]) printf("1\n1 %d\n",maxi); else printf("-1"); return 0; } print(A,0); return 0; } \\ ==== G - Maximum Product ==== 可以意识到,乘积最大的必然是在区间L,R内并且有若干个9的,我们考虑找一个小于R的,最后若干位为9的数字,然后看这个数字是否大于L,如果大于则可以更新答案。 #include using namespace std; typedef long long ll; ll calc(ll a) { ll ans = 1; while (a) { ans = ans*(a%10); a = a/10; } return ans; } ll Pow10(int x) { ll ans = 1; for (int i = 1;i<= x;i++) ans = ans*10; return ans; } int main() { ll a,b; scanf("%lld%lld",&a,&b); ll ans = calc(b); ll tans = b; for (int i = 1;i<= 18;i++) { ll now = b; int newn[20]={0},newlen=0; for (int j = 1;j<= i && now;j++) { if (now >= 10) { if (now%10!=9) now -= 10; newn[++newlen] = 9; } else { newn[++newlen] = now; } now /= 10; } ll tnewn = 0; for (int i = newlen;i>=1;i--) tnewn = tnewn*10+newn[i]; tnewn = now*Pow10(newlen)+tnewn; if (tnewn >= a && calc(tnewn) > ans) { ans = calc(tnewn); tans = tnewn; } } printf("%lld\n",tans); return 0; } \\ ==== I - Archaeological Research ==== 这道题的题意可以转换为要构造一个字符串,给定一些限制条件,这个限制条件是要求$i$和在区间$(j+1,i-1)$之间的字符不能重复,求一个字典序最小的字符串。 我们考虑贪心构造,我们对于$i$位置要选择的字符就是出现在了$1,j$但没有出现在$j+1,i-1$中的字符最小的那一个,这样我们可以维护一个线段树,只保留每种字符最后一次出现的位置,其他出现的都在线段树上置为inf即可。 #include using namespace std; const int inf = 1e9+5; const int N = 3e5+5; int tr[N<<2],last[N],pos[N],tans[N]; void build(int p,int l,int r) { tr[p] = inf; if (l==r) { return;} int mid = (l+r)>>1; build(p<<1,l,mid); build(p<<1|1,mid+1,r); } void Update(int p,int l,int r,int a,int b) { if (l==r) { tr[p] = b; return ; } int mid = (l+r)>>1; if (a<=mid)Update(p<<1,l,mid,a,b); else Update(p<<1|1,mid+1,r,a,b); tr[p] = min(tr[p<<1],tr[p<<1|1]); } int Getans(int p,int l,int r,int a,int b) { if (l>=a&&r<=b) return tr[p]; int ans = inf; int mid = (l+r)>>1; if (a<=mid)ans = min(ans,Getans(p<<1,l,mid,a,b)); if (b>mid)ans = min(ans,Getans(p<<1|1,mid+1,r,a,b)); return ans; } int main() { int n; scanf("%d",&n); int m,x; for (int i = 1;i<= n;i++)pos[i] = i; for (int i = 1;i<= n;i++) { scanf("%d",&m); for (int j = 1;j<= m;j++) { scanf("%d",&x); pos[x] = min(pos[x],i+1); } } build(1,1,n); tans[1] = 1; last[1] = 1; int c = 1; Update(1,1,n,1,1); for (int i = 2;i<= n;i++) { int tmp = Getans(1,1,n,1,pos[i]-1); if (tmp == inf) tans[i] = ++c; else tans[i] = tmp; if (last[tans[i]]) Update(1,1,n,last[tans[i]],inf); Update(1,1,n,i,tans[i]); last[tans[i]] = i; } for (int i = 1;i<= n;i++) printf("%d ",tans[i]); return 0; } \\ ==== K - Toll Roads ==== 一个比较麻烦的树上dp,因为数据范围很小,边权置零的还是一条路径,所以我们可以枚举这条路径的一点,然后以这个点作为根开始dp,先与处理好每个子树中最长的链和子树直径,然后对所有深度小于$k$的点作为路径的另一端点,假设根为root,当前点为x,这时这个树的直径可能是x的子树直径,可能是root到x路径上延展出去的另外的子树的直径,可能是root到x路径上延展出去的另外的两个子树的最长链拼凑,还可能是root到x路径上延展出去的另外的子树的链和当前链的拼凑。多种情况讨论一下就行了。 很多 很多细节。 #include using namespace std; const int N = 5010; const int inf = 1e9+5; struct E {int nxt,to;}e[N<<1]; int head[N],tot,n,k,ans,ansdp,ans1,ans2,root; void add(int x,int y) { e[++tot].nxt = head[x];head[x] = tot;e[tot].to = y; e[++tot].nxt = head[y];head[y] = tot;e[tot].to = x; } int zj[N],lc[N],dep[N]; void dfs(int x,int fa) { dep[x] = dep[fa]+1; zj[x] = lc[x] = 0; for (int i= head[x];i;i=e[i].nxt) if (e[i].to!=fa) { dfs(e[i].to,x); zj[x] = max(max(zj[x],zj[e[i].to]),lc[e[i].to]+lc[x]+1); lc[x] = max(lc[e[i].to]+1,lc[x]); } } void dfs2(int x,int fa,int ZJ,int LC) { if (dep[x] > k) return; int tmp = max(max(ZJ,zj[x]),LC+lc[x]); if (tmp < ans || (tmp == ans && dep[x] < ansdp)) { ansdp = dep[x]; ans = tmp; ans1 = root-1;ans2 = x-1; } int zj1,zj2,lc1,lc2,lc3; zj1 = zj2 = 0; lc1 = lc2 = lc3 = -1; for (int i = head[x];i;i=e[i].nxt) if (e[i].to!=fa) { int y = e[i].to; if (zj[y] > zj2) zj2 = zj[y]; if (zj2 > zj1) swap(zj1,zj2); if (lc[y] > lc3) lc3 = lc[y]; if (lc3 > lc2) swap(lc3,lc2); if (lc2 > lc1) swap(lc2,lc1); } for (int i = head[x];i;i=e[i].nxt) if (e[i].to!=fa) { int nzj1 = lc1+lc2+2; int y = e[i].to; if (lc[y] == lc1) nzj1 = lc2+lc3+2; if (lc[y] == lc2) nzj1 = lc1+lc3+2; int nzj2 = zj1; if (zj[y] == zj1) nzj2 = zj2; int nlc = lc1; if (lc[y] == lc1) nlc = lc2; dfs2(y,x,max(max(nzj2,ZJ),max(nzj1,LC+1+nlc)),max(LC,nlc+1)); } } int main() { scanf("%d%d",&n,&k); int x,y; for (int i = 1;i \\ ===== 比赛总结与反思 =====