A. Tales of a Sort
题意
给定一个序列 {an}。
现在每次操作为 ∀i,ai←max(0,ai−1)。
求进行多少次操作后序列变为有序?
解法
进行模拟,每次遇到一个逆序的就不断进行操作直到这个逆序被消除。
复杂度 O(n2)(可优化至 O(n))。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
   | #include<bits/stdc++.h> using namespace std;
  using ll=long long; using ull=unsigned long long; using pii=pair<int,int>; #define all(x) x.begin(),x.end() #define mem0(x) memset(x,0,sizeof(x)) #define YES puts("YES") #define NO puts("NO") #define Yes puts("Yes") #define No puts("No") #define errorf(...) fprintf(stderr, __VA_ARGS__)  #define endl '\n'
  int read(){     int f=1,x=0;char c=getchar();     while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}     while(isdigit(c)){x=x*10+c-'0';c=getchar();}     return x*f; }
  const int N = 55; int a[N]; void solve(int kase){     int n=read();     for(int i=1;i<=n;i++){         a[i]=read();     }
      ll ans = 0;     for(int i=1;i<n;i++){         if(a[i]>a[i+1]){             ans += a[i];             int k = a[i];             for(int j=1;j<=n;j++){                 a[j]=max(0,a[j]-k);             }         }     }
      printf("%lld\n",ans); }
  int main(){     int T=read(),TT=1;     while(T--){         solve(TT++);     }     return 0; }
 
  | 
 
B. Good Arrays
题意
给定序列 {an},请问是否存在序列 {bn},使得 ∑ai=∑bi 并且 ∀i,ai=bi,bi>0。
解法
假设有 p 个数为 1,剩下 n−p 个数不为 1,它们的和为 sum。
我们给出一个充要条件。当且仅当 p≤sum−(n−p) 时存在。
证明:
- p>sum−(n−p)
此时 p 个 1 至少要变为 2,需要后面的数分出来 p,但是后面的数最多只能分出来 sum−(n−p),所以一定无解。 
- p≤sum−(n−p)
令后面的 n−p 个数每一个都少一,前面的 p 个数每一个都加 1,剩下差的全部加给第一个数。这是一个合法方案。 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
   | #include<bits/stdc++.h> using namespace std;
  using ll=long long; using ull=unsigned long long; using pii=pair<int,int>; #define all(x) x.begin(),x.end() #define mem0(x) memset(x,0,sizeof(x)) #define YES puts("YES") #define NO puts("NO") #define Yes puts("Yes") #define No puts("No") #define errorf(...) fprintf(stderr, __VA_ARGS__)  #define endl '\n'
  int read(){     int f=1,x=0;char c=getchar();     while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}     while(isdigit(c)){x=x*10+c-'0';c=getchar();}     return x*f; }
  void solve(int kase){     int n=read();     vector<int> v;     for(int i=0;i<n;i++){         v.push_back(read());     }
      if(n==1){         NO;         return;     }
      sort(all(v));     int cnt = 0;     ll sum=0;     for(int i=0;i<n;i++){         if(v[i]!=1)sum+=v[i]-1;         else cnt ++;     }
      if(sum>=cnt)YES;     else NO; }
  int main(){     int T=read(),TT=1;     while(T--){         solve(TT++);     }     return 0; }
 
  | 
 
C. To Become Max
题意
给定序列 {an} 和 k。
你最多可以进行 k 次如下操作:
选定 ai<ai+1 的一个 i,然后给 ai 加上 1。
请问序列的最大值最多是多少?
解法
二分答案。考虑如何判断能否达到 x。
若一个位置是 x,那后面至少是 x1,x−2,…,因此我们可以枚举所有的位置,判断能否让这个位置达到 x。
复杂度 O(n2logM)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
   | #include<bits/stdc++.h> using namespace std;
  using ll=long long; using ull=unsigned long long; using pii=pair<int,int>; #define all(x) x.begin(),x.end() #define mem0(x) memset(x,0,sizeof(x)) #define YES puts("YES") #define NO puts("NO") #define Yes puts("Yes") #define No puts("No") #define errorf(...) fprintf(stderr, __VA_ARGS__)  #define endl '\n'
  int read(){     int f=1,x=0;char c=getchar();     while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}     while(isdigit(c)){x=x*10+c-'0';c=getchar();}     return x*f; }
  const int N = 1005; int a[N]; ll s[N]; int n,k;
  bool check(int x){     for(int i=1;i<=n;i++){         int lst = k;         for(int j=i;j<=n;j++){             int need = (x-(j-i))-a[j];             if(need<=0)return true;
              if(lst>=need)lst-=need;             else break;         }     }     return false; }
  void solve(int kase){     n=read(),k=read();     for(int i=1;i<=n;i++){         a[i]=read();         s[i]=s[i-1]+a[i];     }     a[n+1]=-1e8;
      ll l=0,r=2e8;     while(l<r){         int mid=(l+r+1)>>1;         if(check(mid))l=mid;         else r=mid-1;     }
      printf("%lld\n",l); }
  int main(){     int T=read(),TT=1;     while(T--){         solve(TT++);     }     return 0; }
 
  | 
 
D. More Wrong
题意
交互题。
给定一个 1∼n 的排列。每次你可以询问一段连续区间 $[l,r] $内的逆序对个数,每次询问的花费为 (r−l)2,请在不超过花费 5n2 的情况下,找出排列中最大数 n 的所在位置。
解法
考虑分治。
设 f(l,r) 为 [l,r] 中最大值的位置。
当区间长度为 1 时,直接返回 l。
当区间长度为 2 时,询问一次 [l,r] 内的逆序对,得到最大值。
当区间长度大于等于 2 时,先求出左区间和右区间的最大值。然后我们考虑到底哪个才是真正的最大值。
我们用到这个结论:若 [l,r] 和 [l,r−1] 内逆序对个数相同,那么 ar 是 [l,r] 内的最大值,询问两次得到哪个才是最大值。
这个的花费是够的。(证明略)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
   | #include<bits/stdc++.h> using namespace std;
  using ll=long long; using ull=unsigned long long; using pii=pair<int,int>; #define all(x) x.begin(),x.end() #define mem0(x) memset(x,0,sizeof(x)) #define YES puts("YES") #define NO puts("NO") #define Yes puts("Yes") #define No puts("No") #define errorf(...) fprintf(stderr, __VA_ARGS__)  #define endl '\n'
  int read(){     int f=1,x=0;char c=getchar();     while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}     while(isdigit(c)){x=x*10+c-'0';c=getchar();}     return x*f; }
  int query(int l,int r){     if(l==r)return l;     if(r==l+1){         printf("? %d %d\n",l,r);fflush(stdout);         int x=read();         if(x==1)return l;         return r;     }
      int mid = (l+r)>>1;     int lmax = query(l,mid);     int rmax = query(mid+1,r);     printf("? %d %d\n",lmax,rmax);fflush(stdout);     int x1=read();     int x2;     if(lmax!=rmax-1){         printf("? %d %d\n",lmax,rmax-1);fflush(stdout);         x2=read();     }     else x2=0;     if(x1==x2)return rmax;     return lmax; }
 
  void solve(int kase){     int n=read();     printf("! %d\n",query(1,n));fflush(stdout); }
  int main(){     int T=read(),TT=1;     while(T--){         solve(TT++);     }     return 0; }
 
  | 
 
E1. PermuTree (easy version)
题意
有一个根为 1 的树。请你给 1∼n 的每个节点赋一个 1∼n 的互不重复的权值,使得令 au<alca(u,v)<av 的 (u,v) 数量最多,并求出这个数量。
解法
考虑对每个节点单独判断。这是因为内部的大小和外部是没有关系的。无论是什么样的大小关系,都可以构造出合法的。
那么这个节点的值就是子结点中比它小的乘上子节点中比它大的。可以证明子节点的子树中一定要么比根节点小要么比根节点大。
于是可以进行一次背包求出最大值,复杂度 O(n2)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
   | #include<bits/stdc++.h> using namespace std;
  using ll=long long; using ull=unsigned long long; using pii=pair<int,int>; #define all(x) x.begin(),x.end() #define mem0(x) memset(x,0,sizeof(x)) #define YES puts("YES") #define NO puts("NO") #define Yes puts("Yes") #define No puts("No") #define errorf(...) fprintf(stderr, __VA_ARGS__)  #define endl '\n'
  int read(){     int f=1,x=0;char c=getchar();     while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}     while(isdigit(c)){x=x*10+c-'0';c=getchar();}     return x*f; }
  const int N = 5005; vector<int> G[N]; int sz[N];
  ll ans;
  void dfs(int u,int f){     sz[u]=1;     vector<int> s;     int sum = 0;     
      for(int v:G[u]){         if(v==f)continue;         dfs(v,u);         sz[u]+=sz[v];         s.push_back(sz[v]);         sum += sz[v];     }
      if(s.size()<2)return;     bitset<5005> dp;     dp[0]=1;     for(auto p:s){         dp |= dp<<p;     }
      ll v = 0;     for(int i=0;i<sum;i++){         if(dp[i]){             v=max(v,(ll)i*(sum-i));         }     }     ans += v; }
  int main(){     int n=read();     ans = 0;     for(int i=1;i<=n;i++)G[i].clear();
      for(int i=2;i<=n;i++){         int x=read();         G[i].push_back(x);         G[x].push_back(i);     }
      dfs(1,1);
      printf("%lld\n",ans);     return 0; }
 
  |