A. Dense Array
给你
n
n
n 个数的数组,定义数组有效为任意两个相邻的数
m
a
x
(
a
r
r
[
i
]
,
a
r
r
[
i
−
1
]
)
/
m
i
n
(
a
r
r
[
i
]
,
a
r
r
[
i
−
1
]
)
<
=
2
max(arr[i],arr[i-1])/min(arr[i],arr[i-1]) <= 2
max(arr[i],arr[i−1])/min(arr[i],arr[i−1])<=2 那么按照题意check 补数即可,注意要从小的去不断的乘2去构造,大的 / 2 会有精度损失
#include<cstdio>
#include<cstring>
#include<vector>
#include<map>
#include<stack>
#include<string>
#include<iostream>
#include<set>
#include<algorithm>
using namespace std;
#define ll long long
#define dbg(x) cout << #x << " = " << (x) << endl;
#define dbg2(x,y) cout << #x << " = " << (x) << " " << #y << " = " << (y) << endl;
const int MAX_N = 55;
ll arr[MAX_N];
int main()
{
int t,n;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i = 1;i<=n;++i)
{
scanf("%lld",&arr[i]);
}
arr[0] = arr[1];
arr[n+1] = arr[n];
ll ans = 0;
for(int i = 1;i<=n;++i)
{
ll tmp = arr[i];
while(tmp*2<arr[i-1])
{
tmp*=2;
ans++;
}
tmp = arr[i];
while(tmp*2<arr[i+1])
{
tmp*=2;
ans++;
}
}
printf("%d\n",ans);
}
return 0;
}
B. Balanced Remainders
给你
n
n
n 个数,
n
n
n 是
3
3
3 的倍数,问你每次可以加一个数加一,问你最少操作使得
n
n
n 个数每个数 %
3
3
3 所得的数总和分别为
n
/
3
n/3
n/3 ,这题卡了一下 我们发现做法是将 %
3
=
0
3 = 0
3=0 的数加一变为 %
3
=
1
3 = 1
3=1 , 后来觉得跑两次 最终情况一定在这两次之间 得出答案 我们发现只要大小
>
n
/
3
> n/3
>n/3 则需要往下一个数组进,缺少则需要上个数组来 我们跑两次正好模拟了所有情况
#include<cstdio>
#include<cstring>
#include<vector>
#include<map>
#include<stack>
#include<string>
#include<iostream>
#include<set>
#include<algorithm>
using namespace std;
#define ll long long
#define dbg(x) cout << #x << " = " << (x) << endl;
#define dbg2(x,y) cout << #x << " = " << (x) << " " << #y << " = " << (y) << endl;
const int MAX_N = 30025;
int arr[MAX_N];
int cnt[5],brr[5];
int main()
{
int t,n;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
cnt[0] = cnt[1] = cnt[2] = 0;
for(int i = 1;i<=n;++i) scanf("%d",&arr[i]),arr[i] = (arr[i]%3),cnt[arr[i]]++;
int ans = 0;
for(int i = 0;i<3;++i)
{
int tmp =max(cnt[i]-n/3,0);
cnt[i] -= tmp;
if(i!=2) cnt[i+1]+=tmp;
else cnt[0] += tmp;
ans+=tmp;
}
for(int i = 0;i<3;++i)
{
int tmp =max(cnt[i]-n/3,0);
cnt[i] -= tmp;
if(i!=2) cnt[i+1]+=tmp;
else cnt[0] += tmp;
ans+=tmp;
}
printf("%d\n",ans);
}
return 0;
}
C. Sum of Cubes
判断一个数是否是两个立方的和 数小于等于
1
e
12
1e12
1e12 那么我们遍历
1
e
4
1e4
1e4 即可,用
m
p
mp
mp 判断是否存在
#include<cstdio>
#include<cstring>
#include<vector>
#include<map>
#include<stack>
#include<string>
#include<iostream>
#include<set>
#include<algorithm>
using namespace std;
#define ll long long
#define dbg(x) cout << #x << " = " << (x) << endl;
#define dbg2(x,y) cout << #x << " = " << (x) << " " << #y << " = " << (y) << endl;
const int MAX_N = 10005;
map<ll,int> mp;
void init()
{
for(ll i = 1;i<=10000;++i)
{
if(i*i*i>1e12) break;
if(mp.find(i*i*i)==mp.end()) mp[i*i*i] = 0;
else mp[i*i*i]++;
}
}
int main()
{
init();
int t;
ll n;
scanf("%d",&t);
while(t--)
{
scanf("%lld",&n);
bool flag = false;
for(ll i = 1;i<=10000;++i)
{
if(i*i*i>n) break;
if(mp.find(n-i*i*i)!=mp.end())
{
flag = true;
break;
}
}
if(flag) printf("YES\n");
else printf("NO\n");
}
return 0;
}
D. Permutation Transformation
应该是前序遍历 分治即可
#include<cstdio>
#include<cstring>
#include<vector>
#include<map>
#include<stack>
#include<string>
#include<iostream>
#include<set>
#include<algorithm>
using namespace std;
#define ll long long
#define dbg(x) cout << #x << " = " << (x) << endl;
#define dbg2(x,y) cout << #x << " = " << (x) << " " << #y << " = " << (y) << endl;
const int MAX_N = 105;
int arr[MAX_N],ans[MAX_N];
void dfs(int l,int r,int dep)
{
if(l>r) return;
if(l==r) {
ans[l] = dep;
return;
}
int maxx = 0,xb;
for(int i = l;i<=r;++i) if(arr[i]>maxx) maxx = arr[i],xb = i;
ans[xb] = dep;
dfs(l,xb-1,dep+1);
dfs(xb+1,r,dep+1);
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n;
scanf("%d",&n);
for(int i = 1;i<=n;++i) scanf("%d",&arr[i]);
dfs(1,n,0);
for(int i = 1;i<=n;++i) i==n?printf("%d\n",ans[i]):printf("%d ",ans[i]);
}
return 0;
}
E. Accidental Victory
给你
n
n
n 个人 每次选出两个值非空的人 只要一人值大于等于另一个人 则有机会吞下另一个人的值(相等时随机)
问你有可能赢的人的编号 我们维护前缀和,然后值一样的人前缀和应该相同,所以需要倒着更新一次
只要判断前缀和是否能大于等于下个人的下标 则可以判断这人能不能吃下下一个人
我们认为能吃下最大的人的就能赢 所以按逻辑维护一次即可
#include<cstdio>
#include<cstring>
#include<vector>
#include<map>
#include<stack>
#include<string>
#include<iostream>
#include<set>
#include<algorithm>
using namespace std;
#define ll long long
#define dbg(x) cout << #x << " = " << (x) << endl;
#define dbg2(x,y) cout << #x << " = " << (x) << " " << #y << " = " << (y) << endl;
const int MAX_N = 200025;
ll sum[MAX_N];
struct node
{
int x,id;
bool operator < (const node &other) const
{
return x < other.x;
}
}arr[MAX_N];
int uniq[MAX_N];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
set<int> st;
int n,len = 0;
scanf("%d",&n);
for(int i = 1;i<=n;++i) scanf("%d",&arr[i].x),arr[i].id = i;
sort(arr+1,arr+1+n);
for(int i = 1;i<=n;++i)
{
if(i==1||arr[i].x!=arr[i-1].x) uniq[++len] = i;
}
uniq[len+1] = uniq[len];
for(int i = 1;i<=n;++i) sum[i] = sum[i-1] + arr[i].x;
for(int i = n;i>=2;--i)
{
if(arr[i-1].x==arr[i].x) sum[i-1] = sum[i];
}
int xb = 1;
arr[n+1].x = 0;
for(int i = 1;i<=n;++i)
{
while(xb+1<=len+1&&arr[uniq[xb]].x<=arr[i].x) xb++;
if(sum[i]>=arr[uniq[xb]].x) st.insert(arr[i].id);
else st.clear();
}
printf("%d\n",st.size());
for(set<int>::iterator it = st.begin();it!=st.end();++it)
{
printf("%d ",*it);
}
printf("\n");
}
return 0;
}
F. Equalize the Array
其实问题本质是 最大矩形简易版 高度按顺序排好
证明:数为矩形分布的时候则满足条件,那么总面积不变,最大矩形最大则去掉的数最少
#include<cstdio>
#include<cstring>
#include<vector>
#include<map>
#include<stack>
#include<string>
#include<iostream>
#include<set>
#include<algorithm>
using namespace std;
#define ll long long
#define dbg(x) cout << #x << " = " << (x) << endl;
#define dbg2(x,y) cout << #x << " = " << (x) << " " << #y << " = " << (y) << endl;
const int MAX_N = 200025;
int arr[MAX_N],brr[MAX_N];
map<int,int> mp;
multiset<int> st;
int main()
{
int n,t;
scanf("%d",&t);
while(t--)
{
ll ans = 0x3f3f3f3f3f3f3f;
ll sum = 0;
ll cnt = 0;
st.clear();
mp.clear();
scanf("%d",&n);
for(int i = 1;i<=n;++i)
{
scanf("%d",&arr[i]);
brr[i] = arr[i];
if(mp.find(arr[i])==mp.end()) mp[arr[i]] = 1;
else mp[arr[i]]++;
}
sort(brr+1,brr+1+n);
int sz = unique(brr+1,brr+1+n) - (brr+1);
for(int i = 1;i<=sz;++i)
{
st.insert(mp[brr[i]]);
sum+=mp[brr[i]];
}
for(multiset<int>::iterator it= st.begin();it!=st.end();++it)
{
ans = min(ans,sum-1ll*(sz-cnt)*(*it));
cnt++;
}
printf("%lld\n",ans);
}
return 0;
}
G. Old Floppy Drive
我们发现一个性质是说 如果前缀和大于等于查询的
x
x
x 那么扫一次肯定能得到答案,否则我们需要看下第
n
n
n 个前缀和是否大于
0
0
0 , 大于才有贡献 然后我们就能把问题简化为扫一次(多次的用除法得到),我们想得到值大于等于当前
x
x
x 的最左边的数,可以围绕一个
a
n
s
ans
ans 数组 意义是达到此值的最左下标,因为你可能第
3
3
3 的前缀和到了
2
2
2 ,第
4
4
4 的前缀和到了
1
1
1 ,你想要找
1
1
1 直接二分会找到下标
4
4
4 ,其实我们发现下标
3
3
3 才是想要的答案 所以
a
n
s
ans
ans 数组能满足这个需求 收货就是
a
n
s
ans
ans 数组思想挺巧妙的
#include<cstdio>
#include<cstring>
#include<vector>
#include<map>
#include<stack>
#include<string>
#include<iostream>
#include<set>
#include<algorithm>
using namespace std;
#define ll long long
#define dbg(x) cout << #x << " = " << (x) << endl;
#define dbg2(x,y) cout << #x << " = " << (x) << " " << #y << " = " << (y) << endl;
#define dbg3(x,y,z) cout << #x << " = " << (x) << " " << #y << " = " << (y) << " " << #z << " = " << (z) << endl;
const int MAX_N = 200025;
int arr[MAX_N],ans[MAX_N];
ll sum[MAX_N];
struct node
{
ll x;
int id;
bool operator < (const node &other) const
{
return x < other.x;
if(x==other.x) return id > other.id;
}
}brr[MAX_N];
int main()
{
int t,n,m;
ll x;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
for(int i = 1;i<=n;++i)
{
scanf("%d",&arr[i]);
}
for(int i = 1;i<=n;++i)
{
sum[i] = sum[i-1]+arr[i];
brr[i].x = sum[i];
brr[i].id = i;
}
sort(brr+1,brr+1+n);
ans[n+1] = MAX_N;
for(int i = n;i>=1;--i)
{
ans[i] = min(brr[i].id,ans[i+1]);
}
while(m--)
{
scanf("%lld",&x);
if((sum[n]<=0&&brr[n].x<x))
{
printf("-1 ");
continue;
}
if(x<=brr[n].x)
{
int l = 1,r = n;
while(l<=r)
{
int mid = l+r>>1;
if(brr[mid].x>=x) r = mid - 1;
else l = mid + 1;
}
printf("%d ",ans[l]-1);
}
else
{
ll tmp = (x-brr[n].x);
ll cnt = (tmp-1)/sum[n] + 1;
x-=cnt*sum[n];
int l = 1,r = n;
while(l<=r)
{
int mid = l+r>>1;
if(brr[mid].x>=x) r = mid - 1;
else l = mid + 1;
}
printf("%lld ",ans[l]-1+(cnt*n));
}
}
printf("\n");
}
return 0;
}