题目链接:2022年ICPC区域赛济南站
K. Stack Sort
解析:
根据题目意思,每次会将一个栈里的元素弹尽,也就是说我们得让一个栈里元素全是相邻的,且大数先进。
那可以我们先用map存下每个数在数组中的位置。
然后从2枚举到n,判断i的位置与i-1的位置。
首先开始得开一个栈。
如果i-1位置在i后面,说明这时i不用单开一个栈,反之需要多开一个。
所以答案就是统计多少个i在i-1前⾯。
#include <bits/stdc++.h>
using namespace std;
#pragma G++ optimize(2)
#define debug(x) cout << "[debug] " #x << " = " << x << '\n';
#define ull unsigned long long
#define double long double
#define int long long
typedef pair<int, int> PII;
const int inf = 0x3f3f3f3f3f3f3f3f;
const int N = 2e6 + 7;
const int P = 131;
int n, k;
int a[N];
void solve()
{
int n;
cin>>n;
map<int,int>mp;
for (int i=1;i<=n;i++){
cin>>a[i];
mp[a[i]]=i;
}
int ans=1;
for (int i=2;i<=n;i++){
if (mp[i]>mp[i-1]){
ans+=1;
}
}
cout<<ans<<"\n";
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
int T;
T = 1;
cin >> T;
while (T--)
{
solve();
}
return 0;
}
Best Carry Player
解析:
因为无论怎么变换顺序,最后答案都是一样的,进位也是一样的,所以进位次数与顺序无关,因此正常用高精加的方法做就行。
/*
* @Author: Zhouzw
* @LastEditTime: 2024-08-27 15:39:48
*/
#include <bits/stdc++.h>
using namespace std;
#pragma G++ optimize(2)
#define debug(x) cout << "[debug] " #x << " = " << x << '\n';
#define ull unsigned long long
#define double long double
#define int long long
typedef pair<int, int> PII;
const int inf = 0x3f3f3f3f3f3f3f3f;
const int N = 2e6 + 7;
const int P = 131;
int wei1[401],wei2[401];
int cal(int x,int y){
int cnt=0;
for (int i=1;i<40;i++){
wei1[i]=0;
wei2[i]=0;
}
for (int i=1;i<40;i++){
wei1[i]=x%10;
x/=10;
}
for (int i=1;i<40;i++){
wei2[i]=y%10;
y/=10;
}
int jin=0;
for (int i=1;i<40;i++){
wei1[i]=wei1[i]+wei2[i]+jin;
if (wei1[i]>=10){
jin=1;
cnt+=1;
}else{
jin=0;
}
}
return cnt;
}
void solve()
{
int n;
cin>>n;
int sum=0,ans=0;
for (int i=1;i<=n;i++){
int x;
cin>>x;
ans+=cal(sum,x);
sum+=x;
}
cout<<ans<<"\n";
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
int T;
T = 1;
cin >> T;
while (T--)
{
solve();
}
return 0;
}
E. Identical Parity
解析:
这道题赛时过了很多人,我觉得甚至比A、D难,题解看了很久才弄明白。
我们来求对于k来说,n符合的范围。
首先对于k为偶数的情况,因为可以奇数和偶数间隔排完,是恒可以的。
对于k为奇数的情况,我们先假设一组长度为k的序列为x,这样一共n/k组拼起来肯定是好的长序列。
但是因为k为奇数,所以一组长度为k的序列x肯定会多一个奇或偶。
总的奇数肯定是大于等于偶数的,所以我们让奇数多一个,这样n/k组会多n/k个奇数。
所以我们需要用连续n/k-1个偶数补在后面,这样整个序列肯定是好的,而且奇数正好比偶数多一个,也就是符合要求n的最小值。这时我们构造的序列x肯定会以至少t-1个偶数开始,并且t-1<=x。
接下来最大范围,也就是把k/2中剩下的0都用上,然后将相同数量的1也都用上。也就是0 1 0 1这样补,剩下0的数量最多也就是k/2-(t-1),因此能补的1数量也就是k/2-(t-1)。因此n的最大范围是n/k*k+t-1+k/2-(t-1)+k/2-(t-1).
比如n=30,k=9.那么序列可以是
000011111 0000111111 000011111 000
如果n是31,k=9,那么序列是
000101111 000101111 000101111 0001
如果n是33,k=9,那么序列是
000101111 000101111 000101111 000101
而n=29,k=9,是最小情况
000011111 0000111111 000011111 00
也就是在符合要求下每次多一个单个序列x就会0 1 0 1这样构造.
/*
* @Author: Zhouzw
* @LastEditTime: 2024-08-27 15:39:48
*/
#include <bits/stdc++.h>
using namespace std;
#pragma G++ optimize(2)
#define debug(x) cout << "[debug] " #x << " = " << x << '\n';
#define ull unsigned long long
#define double long double
#define int long long
typedef pair<int, int> PII;
const int inf = 0x3f3f3f3f3f3f3f3f;
const int N=1000;
void solve() {
int n,k;
cin>>n>>k;
if (k==1) {
if (n==1){
cout<<"Yes\n";
}else{
cout<<"No\n";
}
} else if (k%2==0) {
cout<<"Yes\n";
} else {
int t=n/k;
int x=k/2;
if (x<t-1) {
cout<<"No\n";
return;
}
if (t*k+t-1<=n&&n<=t*k+(t-1)+x-(t-1)+x-(t-1)) {
cout<<"Yes\n";
} else {
cout<<"No\n";
}
}
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
int T;
T = 1;
cin >> T;
while (T--) {
solve();
}
return 0;
}
A. Tower
解析:
我们要把所有的塔变成一个高度,如果不考虑除2这个操作,那么肯定选择中位数操作最优。
而多了除2这个操作会有什么影响呢,有一些大数变为小数通过这个操作会更快。
VP时看到这个n是500,那么肯定是n的平方外面加个t,因此复杂度就是50050010,然后再加/2的log感觉复杂度很符合。
差不多就想到,枚举一个高度,然后计算其他塔到这个塔的高度的最小操作数,去掉最多的m个。
如果这个塔高于最终高度,我们就需要先就进行除2操作,然后再慢慢减一。
但是枚举的高度怎么确定呢,因为有了除2操作,所以我们可能选小数更快,而不去选中位数,比如2,3,100,100,100。
因此每一个塔的高度都可以是最终的高度,其次向下除2还有有个向下取整的问题,比如4,10,10,10这种情况,我们选5肯定是最优的。因此我们还要将每一个塔除2直到0的高度进行枚举。
/*
* @Author: Zhouzw
* @LastEditTime: 2024-08-27 15:39:48
*/
#include <bits/stdc++.h>
using namespace std;
#pragma G++ optimize(2)
#define debug(x) cout << "[debug] " #x << " = " << x << '\n';
#define ull unsigned long long
#define double long double
#define int long long
typedef pair<int, int> PII;
const int inf = 0x3f3f3f3f3f3f3f3f;
const int N=1000;
int a[N];
void solve() {
int n,m;
cin>>n>>m;
vector<int>v;
for (int i=1; i<=n; i++) {
cin>>a[i];
int x=a[i];
while (x>0) {
v.push_back(x);
x/=2;
}
}
int mn=1e18;
for (int i=0; i<v.size(); i++) {
int ans=0;
vector<int>vv;
for (int j=1; j<=n; j++) {
if (a[j]<=v[i]) {
int cnt=v[i]-a[j];
vv.push_back(cnt);
} else {
int cnt=0;
int x=a[j];
while (x/2>=v[i]) {
x/=2;
cnt+=1;
}
cnt+=min(x-v[i],v[i]-x/2+1);
vv.push_back(cnt);
}
}
sort(vv.begin(),vv.end());
int x=vv.size();
for (int k=0; k<vv.size()-m; k++) {
ans+=vv[k];
}
if (mn>ans) {
mn=min(mn,ans);
}
}
cout<<mn<<'\n';
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
int T;
T = 1;
cin >> T;
while (T--) {
solve();
}
return 0;
}
Frozen Scoreboard
解析:
首先先将已经确定通过的题和罚时减去,剩下还需要过的题a和罚时b。
接下来就是可能会有13个不确定的题,向其中选7道。
我们可以使用二进制枚举,从1枚举到(1<<m),其中如果有a个1,并且1的位置不包含本身就过了的题,那么我们就可以判断它的罚时是否符合。
罚时也是有一个范围的。最小肯定就是封榜后240的时候一发过,最坏就是第299分钟的时候y-1才过,这时可以计算出这a道题总共的最小罚时mn,最大罚时mx,如mn<=b<=mx,那就是符合的。
符合之后我们就需要判断,具体的每一道题需要什么时候通过,封榜后提交多少次。
我们可以先将b-mn。
这时我们遍历将每一道题的提交次数拉满,cs=min(b/20,x-1),以及时间也这样修改,sj=min(b,59).
/*
* @Author: Zhouzw
* @LastEditTime: 2024-08-27 15:39:48
*/
#include <bits/stdc++.h>
using namespace std;
#pragma G++ optimize(2)
#define debug(x) cout << "[debug] " #x << " = " << x << '\n';
#define ull unsigned long long
#define double long double
#define int long long
typedef pair<int, int> PII;
const int inf = 0x3f3f3f3f3f3f3f3f;
const int N=1000;
int a,b;
int cnt;
struct Node {
char op;
int x,y;
} node[N];
int n,m;
int flag[N];
int tim[N];
void input() {
for (int i=0; i<m; i++) {
flag[i]=0;
tim[i]=0;
cin>>node[i].op;
if (node[i].op=='+') {
int cs=0,sj=0,dd=0;
string s;
cin>>s;
for (int j=0; j<s.size(); j++) {
if (s[j]=='/') {
dd=j;
break;
}
cs=cs*10+s[j]-'0';
}
for (int j=dd+1; j<s.size(); j++) {
sj=sj*10+s[j]-'0';
}
node[i].x=cs;
node[i].y=sj;
b=b-(node[i].x-1)*20-node[i].y;
a-=1;
flag[i]=1;
} else if (node[i].op=='-') {
cin>>node[i].x;
} else if (node[i].op=='.') {
continue;
} else {
flag[i]=4;
cin>>node[i].x>>node[i].y;
node[i].x=node[i].y-node[i].x;
}
}
}
int check() {
if (a<0||b<0) {
return 0;
}
//cout<<(1<<m)<<"****"<<'\n';
for (int i=0; i<(1<<m); i++) {
int num=0;
int vis=0;
for (int j=0; j<m; j++) {
if (((i>>j)&1)==0) {
continue;
}
num+=1;
}
//cout<<b<<"***\n";
if (num!=a) {
continue;
}
for (int j=0; j<m; j++) {
if (((i>>j)&1)==1&&flag[j]!=4) {//只保留有?的题
vis=1;
break;
}
}
if (vis) {
continue;
}
int mn=0,mx=0;
for (int j=0; j<m; j++) {
if (((i>>j)&1)==1) {
mn+=(node[j].x*20)+240;
mx+=(node[j].y-1)*20+299;
}
}
if (b>=mn&&b<=mx) {
b-=mn;
for (int j=0; j<m; j++) {
if (((i>>j)&1)==1) {
int cs=min(b/20,node[j].y-node[j].x-1);
node[j].x+=cs+1;
b-=(cs*20);
int sj=min(b,1ll*59);
tim[j]=240+sj;
b-=sj;
flag[j]=1;
}
}
return 1;
}
}
return 0;
}
void print() {
cout<<"Yes\n";
for (int i=0; i<m; i++) {
if (node[i].op=='+') {
cout<<node[i].op<<" "<<node[i].x<<"/"<<node[i].y<<"\n";
} else if (node[i].op=='-') {
cout<<node[i].op<<" "<<node[i].x<<"\n";
} else if (node[i].op=='.') {
cout<<'.'<<'\n';
} else {
if (flag[i]==1) {
cout<<"+ "<<node[i].x<<"/"<<tim[i]<<"\n";
} else {
cout<<"- "<<node[i].y<<"\n";
}
}
}
}
void solve() {
cin>>n>>m;
for (int i=1; i<=n; i++) { //第i支队伍
cin>>a>>b;
input();
if (check()) {
print();
} else {
cout<<"No\n";
}
}
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
int T;
T = 1;
// cin >> T;
while (T--) {
solve();
}
return 0;
}```