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; }
|