第一题:1759G - Restore the Permutation
大意:
输入 T(≤1e4) 表示 T 组数据。所有数据的 n 之和 ≤2e5。
每组数据输入偶数 n(2≤n≤2e5) 和长为 n/2 的数组 b(1≤b[i]≤n),下标从 1 开始。
构造一个长为 n 的 1~n 的排列 p(下标从 1 开始),满足 max(p[2*i-1],p[2*i]) = b[i]。
如果无法构造,输出 -1,否则输出字典序最小的 p。
思路1:由题意得,对于b[i],由于max(p[2*i-1],p[2*i]) = b[i],p为一个排列,故另一个数必小于b[i],为了保证字典序最小,故必有p[2*i-1]<p[2*i] ,即 p[2*i] = b[i] ;故对于每个p[2k] ,k为整数,需要寻找一个 属于[1,b[i] ) 且未被使用过的数。为了保证字典序最小,故需要从下标2n开始往1遍历,每次寻找一个属于[1,b[i] ) 且未被使用过的数中最大的数。如果找得到这样的数则依次填写直至填写完毕,则完成构造。若没找到满足要求的数,则说明无法构造。
思路2:(灵神算法交流群群友的思路) 贪心并查集。 通过union( v,v-1) 把v并到v-1的子树下,则find(v)则为第一个小于v且未被使用过的数。算法复杂度为o(n)。
思路一代码:
#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
#include <map>
#include <set>
#define x first
#define y second
#define PII pair<int,int>
using namespace std;
typedef long long LL ;
const int N = 2e5 +10 ,mod = 1e9 +7 ;
LL a[N] ,b[N] ,w[N];
bool st[N] ;
int main()
{
int T ;cin >> T;
while( T--){
int n ;
cin >> n ;
memset( st, false ,sizeof st) ;
bool flag= true;
for(int i= 1;i <=n/2; ++i){
cin>> a[i*2] ;
if( st[ a[i*2] ] ) flag =false;
if( a[i*2] <1 || a[i*2] > 2*n ) flag =false;
st[a[i*2] ] = true;
}
if( !flag ){
cout<<-1<<endl; continue;
}
//cout<<"????";
set<int> s ;
for(int i =1; i<=n *2 ; ++i) if( !st[i]) s.insert( i ) ;
for(int i = n ; i> 0 ; i -=2) {
auto t = s.lower_bound( a[i]);
//cout<< a[i] <<" "<<*t<<endl;
if( t == s.begin()) {
flag = false; break;
}
--t;
a[i-1] = *t;
s.erase( t) ;
}
if( !flag) cout<<-1<<endl;
else {
for(int i =1;i <= n ; ++i)
cout<<a[i] <<" ";cout<<endl;
}
}
return 0 ;
}
问题二:1693B - Fake Plastic Trees
大意:
输入 T(≤1e3) 表示 T 组数据。所有数据的 n 之和 ≤2e5。
每组数据输入 n(2≤n≤2e5) 表示一棵 n 个节点的树,编号从 1 开始,1 为根节点。
然后输入 p[2],p[3],...,p[n],其中 p[i] 表示 i 的父节点。
然后输入 n 行,其中第 i 行输入两个数 l 和 r,表示第 i 个节点值的目标范围 [l,r]。
初始时,所有节点值均为 0。
每次操作你可以选择一条从 1 开始的路径,把路径上的每个节点值都加上一个数。要求这些数按照路径的顺序,形成一个递增序列。(可以相等,可以等于 0,例如 [0,0,1,3,3])
要使所有节点值都在对应的范围内,至少要操作多少次?
思路:贪心。为了每个节点都符合要求,最糟糕的情况是每个节点都操作一次。而为了解决某个节点的同时“顺带”完成另一个节点的要求,显然,需要我们另一个节点为操作节点的父节点。故我们需要从根节点开始考虑。当考虑一个根节点时,为了保证父节点的贡献范围尽可能的大,我们可以把某个根节点的值赋为范围最大值。当节点t的子节点操作完成时,计算由子节点产生的贡献k。若k大于范围最大值,显然只能让t的值为范围最大值r[t] , 当k在范围中时, t的值只能为k;当小于范围最小值l[t]时,我们需要对该节点进行一次操作,同时为了保证父节点的贡献范围尽可能的大,需要赋值为范围最大值。当所有节点计算完成后,操作数即为结果。
代码一:dfs求解
#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
#include <unordered_map>
#define x first
#define y second
#define PII pair<int,int>
using namespace std;
typedef long long LL ;
const LL N = 3e5 +10 ,mod = 1e9 +7 ;
LL fa[N] ;
LL w[N] ;
PII b[N] ;
int h[N] , ne[N] ,e[N] , idx ;
int ans ;
void add( int a, int b ){
e[idx] = b , ne[idx] = h[a] , h[a] = idx ++ ;
}
LL dfs( int u ,int fa){
LL res = 0 ;
for(int i = h[u] ; ~i ;i =ne[i]){
int j = e[i] ;
res += dfs( j , u );
}
if( res >= b[u].x) {
return min( res, (LL)b[u].y) ;
}
//cout<<"--"<<u<<" "<<res <<endl;
ans++;
w[u] = b[u].y;
return w[u] ;
}
int main()
{
int T ;cin >> T;
while( T--){
ans = 0 ;idx = 0;
int n ;
cin >> n ;
memset( w, 0 ,sizeof w) ;
memset( h ,-1, sizeof h ) ;
for(int i =2; i <= n ; ++i){
cin >>fa[i] ;
add( fa[i] , i) ;
}
for(int i =1; i <=n ; ++i){
cin >> b[i].x >> b[i].y ;
}
dfs( 1, -1 ) ;
cout <<ans <<endl;
}
return 0 ;
}
代码二:拓扑排序
#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
#include <unordered_map>
#define x first
#define y second
#define PII pair<int,int>
using namespace std;
typedef long long LL ;
const LL N = 3e5 +10 ,mod = 1e9 +7 ;
LL deg[N] ,fa[N];
LL w[N] ;
pair<LL,LL> b[N] ;
int h[N] , ne[N] ,e[N] , idx ;
int ans ;
int main()
{
int T ;cin >> T;
while( T--){
ans = 0 ;idx = 0;
int n ;
cin >> n ;
memset( w, 0 ,sizeof w) ;
memset( h ,-1, sizeof h ) ;
memset( deg, 0 , sizeof deg) ;
for(int i =2; i <= n ; ++i){
int x;
cin >>x ;
deg[ x] ++ ; fa[i] = x ;
}
for(int i =1; i <=n ; ++i){
cin >> b[i].x >> b[i].y ;
}
queue<int> q;
for(int i=1; i<=n ; ++i) if( !deg[i]) q.push( i) ;
int res= 0;
while( q.size() ){
int t = q.front( ) ;q.pop() ;
//cout<<"--"<<t<<endl;
if( w[t] >= b[t].x) w[t] = min( w[t] ,b[t].y) ;
else {
++res ; w[t] = b[t].y;
}
deg[ fa[t] ] --;
w[ fa[t]] += w[t] ;
if( !deg[fa[t]]) q.push( fa[t]) ;
}
cout<< res <<endl;
}
return 0 ;
}
问题三:1800D - Remove Two Letters
大意:已知长度为n的字符串s,若删除两个连续的字符,问最终能产生多少种字符串。
思路:举个例子,对于字符串aba,无论如何删除,都能产生一种。故我们考虑在什么情况下,会产生重复的一种。显然,对于连续的ai,ai+1,ai+2,若ai = ai+2,则会产生一种重复。故我们假设总共有n-1种不同字符串,每当遇到ai = ai+2时,则减去一个重复的字符串数量。
代码:
#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
#include <map>
#define x first
#define y second
#define PII pair<int,int>
using namespace std;
typedef long long LL ;
const LL N = 3e5 +10 ,mod = 1e9 +7 ;
int main()
{
int T ;cin >> T;
while( T--){
int n ;
cin >> n ;
string s;
int res = n-1;
cin >>s ;
for(int i =0 ; i+2 <n ; ++i){
if( s[i] == s[i+2]) --res;
}
cout<< res <<endl;
}
return 0 ;
}
问题四:1799B - Equalize by Divide
大意:现给你一个长度为n的数组a, 现在你可以进行一次操作:选择i,j 且i!=j ,使得 a[i] = [a[i]/a[j] ,a[i]会向上取整,问需要多少次操作,才能使得a数组中所有元素都相等,并输出每次操作的i,j。
思路:每次选定数组中最大值与最小值,若两者相等,则说明完成要求。若二者不相等,①最小值为1,由于任何数除以1不变,故不可能完成要求;②最小值不为1,则令最大值赋值为 [a[i]/a[j]。重复以上步骤,直至是否完成要求。
代码:
#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
#include <map>
#define x first
#define y second
#define PII pair<int,int>
using namespace std;
typedef long long LL ;
const int N = 2e5 +10 ,mod = 1e9 +7 ;
int a[N] ;
PII res[N] ;
int main()
{
int T ;cin >> T;
while( T--){
int n ;
cin >> n ;
for(int i = 1; i<=n ; ++i){
cin >>a[i];
}
int cnt = 0 ;
while(1){
int mmin = 1, mmax =1;
for(int i=2; i<=n; ++i) {
if( a[mmin ] > a[i]) mmin = i ;
if( a[mmax] < a[i]) mmax = i ;
}
//cout<<mmax <<" " <<mmin<<endl;
if( a[mmin] == a[mmax]) break;
if( a[mmin] == 1){
cnt = -1; break;
}
if( a[mmax] % a[mmin] == 0) a[mmax] /= a[mmin] ;
else a[mmax] = a[mmax]/a[mmin] + 1;
res[cnt++] = { mmax , mmin} ;
}
if( cnt == -1 || cnt == 0 ) cout<< cnt <<endl;
else cout<<cnt <<endl;
for(int i = 0; i <cnt; ++i)
cout<< res[i].x <<" "<<res[i].y <<endl;
}
return 0 ;
}
大意:已知n个茶,n个品茶师,每个品茶师一次只能喝a[i] 份茶,每喝一次则需要向前走动一位,即第一次,第i位品茶师会品茶第i个茶,第2次会品茶第i-1个.问,所有品茶师都无法再喝茶时,每位品茶师喝了多少份茶。
思路:对于第i份茶,会被i~n的品茶师品尝,故我们需要找到从i到k位品茶师的茶量大于i的分量的k,将i~k位品茶师傅已品尝最大份量的次数都加1,若有过剩,则将剩余分量给k+1位品茶师傅。
当计算第i位的品茶师傅的品尝茶量时,用已品尝茶量+已品尝最大份量的次数* 最大品茶量a[i]即可
代码:
#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
#include <map>
#define x first
#define y second
#define PII pair<int,int>
using namespace std;
typedef long long LL ;
const int N = 2e5 +10 ,mod = 1e9 +7 ;
LL a[N] ,b[N] ,w[N];
LL sum[N] ,p[N];
int main()
{
int T ;cin >> T;
while( T--){
int n ;
cin >> n ;
memset( w , 0 , sizeof w) ;
memset( p , 0, sizeof p) ;
for(int i = 1; i<=n ; ++i) cin >>a[i];
for(int i= 1; i <=n; ++i){
cin >>b[i] ; sum[i] = sum[i-1] +b[i ];
}
for(int i = 1;i <=n; ++i){
int cnt= upper_bound( sum+i, sum+1+n , a[i] +sum[i-1]) - sum;
p[i] += 1; p[cnt]-=1;
w[cnt] += a[i] -sum[cnt-1]+sum[i-1] ;
//cout<< i <<" "<<cnt-1 <<" "<<w[cnt] <<endl;
}
LL ce = 0;
for(int i = 1;i <=n; ++i){
ce += p[i] ;
w[i] += ce*b[i] ;
cout<<w[i] <<" " ;
}
cout<<endl;
}
return 0 ;
}
题目六:1794C - Scoring Subsequences
大意:给定长度为n的数组a, a是非递减数组。对于s[1..d],称其所有子串中 s[i..k] (1<=i,k<=d)的得分 si*si+1 *...*s[k] / (k-i+1)! 的最大值 ,需要输出对于不同的d,其对应得分最大时的子串长度。
思路:由于a非递减,对于d,最大得分最大可能是a[d]/1 ,右边界为d ,若左边界向前移动一位,则会使得分乘上a[d-1]/2 。 故可得,若向前移动 i位,会使得分乘上 a[d-i]/(i+1) 。当 a[d-i]/(i+1)<1时,显然不是最优结果。故我们可二分[1,d-1],找到第一个 a[d-i]/(i+1)>=1的i,则答案是i+1。
代码:
#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
#include <map>
#include <set>
#define x first
#define y second
#define PII pair<int,int>
using namespace std;
typedef long long LL ;
const int N = 2e5 +10 ,mod = 1e9 +7 ;
LL a[N] ,b[N] ,w[N];
bool st[N] ;
int main()
{
int T ;cin >> T;
while( T--){
int n ;
cin >> n ;
for(int i = 1 ; i <=n ; ++i)
cin >> a[ i] ;
cout<<1<<' ' ;
for(int i =2 ; i<=n ; ++i){
int l = 1,r = i-1, t = i ;
while( l <=r ) {
int mid = ( l+r ) /2;
if( a[mid ] /(i-mid +1) >= 1) {
t =mid;
r= mid-1;
}else l =mid+1;
}
cout<< i-t+1 <<" ";
}
cout<<endl;
}
return 0 ;
}
大意:给定一个长度为n的排列a,输出a[l],a[r]不等于a[l..r]中的最大值和最小值时的l和r。
思路:通过哈希表,记录某个数是否已经不在[l,r]中,并维护区间的最值。从a[1,n]开始,令l=1,r=n,若a[l],a[r]不等于a[l..r]中的最大值和最小值则输出。否则,删除等于最值的边界,并更新最值,直至 l >r 或符合题意。
代码:
#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
#include <map>
#include <set>
#define x first
#define y second
#define PII pair<int,int>
using namespace std;
typedef long long LL ;
const int N = 2e5 +10 ,mod = 1e9 +7 ;
LL a[N] ,b[N] ,w[N];
bool st[N] ;
int main()
{
int T ;cin >> T;
while( T--){
int n ;
cin >> n ;
for(int i = 1 ; i <=n ; ++i)
cin >> a[ i] ;
memset( st, false, sizeof st) ;
int l = 1 ,r = n , Max = n , Min = 1;
while( l <=r ){
//cout<<l <<" " <<r <<endl;
if( a[r] != Max && a[r] != Min &&a[l] != Max && a[l] != Min) break;
if( a[r] == Max || a[r] == Min) {
st[ a[r]] = true; r--;
}
if( a[l] == Max || a[l] == Min) st[a[l]] = true,l++;
while( st[Max] ) Max--;
while( st[Min]) Min++;
}
if( l<r ) cout<< l<<" " <<r <<endl;
else cout<<-1<<endl;
}
return 0 ;
}