题目大意:
有一个操作,把
a
i
a_i
ai 换成
a
j
a_j
aj。
给
n
n
n 和
k
k
k 和序列
a
i
a_i
ai,求前
k
k
k 个值的和的最小值的时候,最小的操作次数。
题解思路:
贪心地想,因为
a
i
a_i
ai 是个排列(1-n),所以前
k
k
k 个值的和的最小值是1-k,那么就检查一下,前
k
k
k 个数中,有多少个数字不是1-k之中的就可以了
代码如下:
#include <bits/stdc++.h>
#define rep(a,b,c) for(int a=b;a<=c;a++)
#define dec(a,b,c) for(int a=b;a>=c;a--)
#define x first
#define y second
#define pb push_back
#define LL long long
#define ios ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define INF 0x3f3f3f3f
#define PII pair<int,int>
#define pi acos(-1)
#define PIP pair<int,pair<int,int>>
using namespace std;
const int N=1100;
int a[N];
void solve()
{
int n,k;
cin>>n>>k;
rep(i,1,n)
cin>>a[i];
map<int,int> mp;
rep(i,1,k)
mp[a[i]]++;
int ans=0;
rep(i,1,k)
if(!mp[i])
ans++;
cout<<ans<<endl;
}
题目大意:
给一个n,求
∑
i
=
1
n
l
c
m
(
i
,
a
i
)
\sum_{i=1}^nlcm(i,a_i)
∑i=1nlcm(i,ai)最大时,ai的排列
解题思路:
规律:倒着开始枚举,交换相邻两个数字就可以了,变成了
奇数:
1
,
3
,
2
,
5
,
4....
n
,
n
−
1
1,3,2,5,4....n,n-1
1,3,2,5,4....n,n−1
偶数:
2
,
1
,
4
,
3
,
6
,
5...
n
,
n
−
1
2,1,4,3,6,5...n,n-1
2,1,4,3,6,5...n,n−1
打表法:通过手动模拟10个数字的 a i a_i ai ,发现规律(我就是这样做的)
不是很完善的证明:
首先,对于
l
c
m
(
a
,
b
)
lcm(a,b)
lcm(a,b)来说,最优的情况是
l
c
m
(
a
,
b
)
=
a
×
b
lcm(a,b)=a×b
lcm(a,b)=a×b
其次,
l
c
m
(
n
,
n
−
1
)
=
n
×
(
n
−
1
)
lcm(n,n-1)=n×(n-1)
lcm(n,n−1)=n×(n−1)
所以,对于任意
a
i
a_i
ai 的组合来讲,最优的解是指交换相邻的两个数字即可
因为,当下,
n
×
(
n
−
1
)
n×(n-1)
n×(n−1) 必定大于任何其他n*m的组合。当
n
→
∞
{n\to\infty}
n→∞时,和的上界对最大的
l
c
m
lcm
lcm 的大小有很大的关系,所以
n
×
(
n
−
1
)
n×(n-1)
n×(n−1) 绝对是当下最优的解,同理
(
n
−
2
)
×
(
n
−
2
)
(n-2)×(n-2)
(n−2)×(n−2)。。。。
代码如下:
#include <bits/stdc++.h>
#define rep(a,b,c) for(int a=b;a<=c;a++)
#define dec(a,b,c) for(int a=b;a>=c;a--)
#define x first
#define y second
#define pb push_back
#define LL long long
#define ios ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define INF 0x3f3f3f3f
#define PII pair<int,int>
#define pi acos(-1)
#define PIP pair<int,pair<int,int>>
using namespace std;
const int N=2e5+10;
int a[N];
void solve()
{
int n;
cin>>n;
rep(i,1,n)
a[i]=i;
for(int i=n;i>1;i-=2)
swap(a[i],a[i-1]);
rep(i,1,n)
cout<<a[i]<<" ";
cout<<endl;
}
题目大意:
一个操作:选一个数字
x
x
x,然后让所有等于
x
x
x 的
a
i
a_i
ai 都变成
0
0
0
给
n
n
n 和序列
a
i
a_i
ai ,求让序列变成非递减的有序序列的最小操作次数
解题思路:
首先,需要两样东西,
①每一种数字最后一次出现的坐标(用map存一下就行)
②精确地知道在某一个坐标及之前,有多少个不同的数字(用前缀和维护一下就行)
接下来只要确定,最后一个,有递减趋势的位置,的数字,最后一次出现的位置,的坐标就可以了
然后从后往前枚举,找到最后一个有递减趋势的坐标
i
d
x
idx
idx,然后把
i
d
x
idx
idx 及之前的所有数字全部改成0就可以了(就是输出idx及之前有多少个不一样的数字)
代码如下:
#include <bits/stdc++.h>
#define rep(a,b,c) for(int a=b;a<=c;a++)
#define dec(a,b,c) for(int a=b;a>=c;a--)
#define x first
#define y second
#define pb push_back
#define LL long long
#define ios ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define INF 0x3f3f3f3f
#define PII pair<int,int>
#define pi acos(-1)
#define PIP pair<int,pair<int,int>>
using namespace std;
const int N=1e5+10;
int a[N];
int s[N],st[N];
void solve()
{
int n;
cin>>n;
rep(i,1,n)
{
cin>>a[i];
s[i]=st[i]=0;
}
map<int,int> idx;
rep(i,1,n)
{
idx[a[i]]=max(idx[a[i]],i);
s[i]=s[i-1];
if(mp[a[i]]==0) s[i]++;
}
int k=0;
rep(i,1,n)
{
if(a[i]<a[i-1])
{
st[k]++;
}
k=max(idx[a[i]],k);
}
dec(i,n,1)
if(st[i])
{
cout<<s[i]<<endl;
return;
}
cout<<0<<endl;
}
题目大意:
一个操作:可以把一条边的值修改为任意数字
给
n
n
n 和
k
k
k 还有序列
a
i
a_i
ai ,他们一次连成一个圈
d(l,r)表示l到r的最小的边的长度,如果l到r直连并不是最优解,就可以l到1,然后1到r这样走
问修改k次后,最长的d(l,r)是多少
解题思路:
首先,贪心地思考,就可以得出下式
d
(
l
,
r
)
=
m
i
n
(
d
(
l
,
r
),
2
∗
d
(
1
,
l
−
1
),
2
∗
d
(
r
+
2
,
n
))
d(l,r)=min(d(l,r),2*d(1,l-1),2*d(r+2,n))
d(l,r)=min(d(l,r),2∗d(1,l−1),2∗d(r+2,n))这样能求出来最优解
由此,可以二分答案
当
2
×
a
i
<
m
i
d
2×a_i<mid
2×ai<mid 的时候,就需要修改,记录一下,求个前缀和,求个后缀和,表示修改次数
如果
a
i
a_i
ai 小于
m
i
d
mid
mid ,就修改,
a
i
+
1
<
m
i
d
a_i+1<mid
ai+1<mid,就需要修改。
修改保证剩余边最小值足够大就可以了,所以直接把需要修改的边修改为
1
0
9
10^9
109 就可以
代码如下:
#include <bits/stdc++.h>
#define rep(a,b,c) for(int a=b;a<=c;a++)
#define dec(a,b,c) for(int a=b;a>=c;a--)
#define x first
#define y second
#define pb push_back
#define LL long long
#define ios ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define INF 0x3f3f3f3f
#define PII pair<int,int>
#define pi acos(-1)
#define PIP pair<int,pair<int,int>>
using namespace std;
const int N=1e5+10;
int a[N];
int s[N],st[N];
int n,m;
bool check(int mid)
{
int s[N],f[N];
s[0]=f[n+1]=0;
rep(i,1,n)
{
s[i]=s[i-1];
if(2*a[i]<mid)
s[i]++;
}
dec(i,n,1)
{
f[i]=f[i+1];
if(2*a[i]<mid)
f[i]++;
}
rep(i,2,n)
{
int k=0;
if(a[i]<mid) k++;
if(a[i-1]<mid) k++;
if(s[i-2]+f[i+1]+k<=m)
return 1;
}
dec(i,n-1,1)
{
int k=0;
if(a[i]<mid) k++;
if(a[i+1]<mid) k++;
if(k+s[i-1]+f[i+2]<=m)
return 1;
}
return false;
}
void solve()
{
cout<<endl;
cin>>n>>m;
rep(i,1,n)
cin>>a[i];
int l=1,r=1000000000;
while(l<r)
{
int mid=l+r+1>>1;
if(check(mid))
l=mid;
else r=mid-1;
}
cout<<l<<endl;
}
我只能说,这次生病给我病麻了,发烧了得四五天,打退烧针都不管用,后来吊了五天吊瓶,现在咳嗽还没好,头疼+头晕。
本来不想打的,但是想了想还是要训练嘛,就打了小号,结果出奇的把D给a掉了,一开始想着是图论,我图论知识基本为零,想了想发现是个二分,写完就开始找bug,找了半小时bug,忘记反着过一遍了,改了之后压线过的。
没想到啊,复健第一场,打大号都上分了,我打的小号。。。