于是问题转化成了:已知序列 A 1 , A 2 . . . A n A_1 , A_2 . . . A_n A1,A2...An,找到一个单调递增的序列 X 1 , X 2 . . . X n X_1 , X_2 . . . X_n X1,X2...Xn,使得 ∑ i = 1 n ( A i − X i ) 2 \sum^{n}_{i=1} (A_i-X_i)^2 ∑i=1n(Ai−Xi)2 最小,求这个最小值。
#include<cstdio>#include<algorithm>#include<cstring>#include<queue>usingnamespace std;typedef pair<int,int> P;constint maxn =400010, maxm =1000010;int h[maxn], e[maxm], ne[maxm], idx;int N, din[maxn], w[maxn];voidadd(int a,int b){
e[idx]= b, ne[idx]= h[a], h[a]= idx++;}
priority_queue<P, vector<P>, greater<P>> que;inttoposort(){
int res =0, tot = N;for(int i =1; i <= N; i++){
if(!din[i]) que.push({
w[i], i });}while(que.size()){
auto p = que.top(); que.pop();int u = p.second;
tot--;
res =max(res, tot + w[u]);for(int i = h[u]; i !=-1; i = ne[i]){
int v = e[i];
din[v]--;if(!din[v]) que.push({
w[v], v });}}return res;}intmain(){
scanf("%d",&N);memset(h,-1,sizeof h);for(int i =1; i <= N; i++){
int m;scanf("%d%d",&w[i],&m);for(int j =0; j < m; j++){
int x;scanf("%d",&x);add(i, x);
din[x]++;}}printf("%d\n",toposort());return0;}
首先处理终点与起点的问题。这个题从后往前构造不如从前往后构造简单。所以我们假设起点是 (0, 0) ,最后走到终点 ( x 0 , y 0 ) (x_0,y_0) (x0,y0),那么把所有点的坐标都 ( x − x 0 , y − y 0 ) (x - x_0, y - y_0) (x−x0,y−y0),就ok了。
然后,考虑构造问题。其实很简单,只要不碰见 “LRLR…, UDUD…” 两种情况,每次移动距离 d 都 + 10,然后把木块儿放在前方就可以。而上述两种情况,移动距离不增加即可,最后把木块儿去重就行。
#include<iostream>#include<cstring>#include<algorithm>#include<string>#include<vector>usingnamespace std;typedef pair<int,int> P;intmain(){
string s;
cin >> s;int n = s.length();//处理无解情况if(n >=3&& s[n -3]== s[n -1]){
if(s[n -2]=='R'&& s[n -1]=='L'|| s[n -2]=='L'&& s[n -1]=='R'|| s[n -2]=='U'&& s[n -1]=='D'|| s[n -2]=='D'&& s[n -1]=='U'){
puts("impossible");return0;}}int x =0, y =0, d =10;
vector<P> ans;for(int i =0; i < n; i++){
//模拟小球滚动和放木块儿的过程//x += d 这种是不可以的。我估计原因应该是,应该让小球离中心越来越远,让小球直接跳到某个位置。//而这种 x += d 的,画图会发现,走一个z字形之类的在原路返回时,会撞到之前的木块儿上。if(s[i]=='L'){
x =-d;
ans.push_back({
x -1, y });}elseif(s[i]=='R'){
x = d;
ans.push_back({
x +1, y });}elseif(s[i]=='U'){
y = d;
ans.push_back({
x, y +1});}else{
y =-d;
ans.push_back({
x, y -1});}//d 在不会发生来回滚动的情况下才增加if(i +1< n){
if(s[i]=='R'&& s[i +1]=='L'|| s[i]=='L'&& s[i +1]=='R'|| s[i]=='U'&& s[i +1]=='D'|| s[i]=='D'&& s[i +1]=='U')continue;
d +=10;}}sort(ans.begin(), ans.end());
ans.erase(unique(ans.begin(), ans.end()), ans.end());printf("%d %d\n",-x,-y);printf("%d\n", ans.size());for(auto p : ans){
printf("%d %d\n", p.first - x, p.second - y);}return0;}
F. Jinxed Betting
题意:给定一些数,每次可以对这些数进行操作,一次操作为:设最大的数有 a 个,将最大的 ⌊ a 2 ⌋ \lfloor \frac{a}{2} \rfloor ⌊2a⌋个数,以及其他所有非最大的数均加 1。再给定一个目标 t,问至多操作几次,可以保证这些数中的最大者不超过 t.
思路:这个就是分类讨论,设最大数 x 1 x_1 x1 有 a 1 a_1 a1 个,次大数 x 2 x_2 x2 有 a 2 a_2 a2 个;可以发现,如果次大数赶上最大数需要的时间是 ( x 1 − x 2 ) ∗ ( l o g 2 a 1 + 1 ) (x_1-x_2)*(log_2a_1+1) (x1−x2)∗(log2a1+1),而最大数赶上 t 的时间是 x 0 − x 1 + ( x 0 − x 1 ) / v x_0 - x_1 + (x_0 - x_1) / v x0−x1+(x0−x1)/v.
那么我们可以分三种情况讨论:
当前只剩下一种数字,即只剩下最大数 x 1 x_1 x1,可以把答案直接加上
x 1 x_1 x1 赶上 t t t 的时间比 x 2 x_2 x2 赶上 x 1 x_1 x1 的时间要短,可以把答案直接加上
x 1 x_1 x1 赶上 t t t 的时间比 x 2 x_2 x2 赶上 x 1 x_1 x1 的时间要长,需要将最大数和次大数合并
#include<iostream>#include<algorithm>#include<cstring>#include<cmath>typedeflonglong ll;usingnamespace std;constint maxn =100010;typedef pair<ll, ll> P;#define x first#define y second
P stk[maxn];
ll a[maxn];int N;intmain(){
scanf("%d",&N);for(int i =0; i < N; i++)scanf("%lld",&a[i]);sort(a, a + N);int tt =0;
stk[++tt]={
a[0],1};for(int i =1; i +1< N; i++){
if(stk[tt].x == a[i]) stk[tt].y++;else stk[++tt]={
a[i],1};}
ll ans =0, x0 = a[N -1];while(1){
ll x1 = stk[tt].x, a1 = stk[tt].y;
ll u =(int)log2(a1)+1, v =(int)log2(a1);
ll m =0;if(v !=0) m =(x0 - x1)/ v;
ll t1 = x0 - x1 + m;//(1)当前只剩下一种数字,即只剩下最大数 x1if(tt ==1){
ans += t1;break;}//这里需要注意,x2应该加上ans,即现在经过的时间,因为 stk[tt - 1] 的答案并没有更新
ll x2 = stk[tt -1].x + ans, a2 = stk[tt -1].y;
ll t2 =(x1 - x2)* u;//(2)x1 赶上 t 的时间比 x2 赶上 x1 的时间要短if(v && t1 <= t2){
ans += t1;break;}//(3)x1 赶上 t 的时间比 x2 赶上 x1 的时间要长else{
ans += t2;auto p1 = stk[tt--], p2 = stk[tt--];
P p ={
p2.x + ans, p1.y + p2.y };
stk[++tt]= p;}}printf("%lld\n", ans);return0;}