1320A. Journey Planning(小思维)
题意:
给定一个数列ai。
从中找一个子序列,满足i>j
, i-j=a[i]-a[j]
。
这样的子序列和最大为多少?
思路:
满足i-j=a[i]-a[j]
,即满足i-a[i]=j-a[j]
。
然后map记录值相同的总和。取最大。
就是一个简单的公式变形,一开始还想着暴搜,dp。。
Code:
const int N = 200010, mod = 1e9+7;
ll T, n, m, a[N];
ll sum,ans;
bool f[N];
int main(){
Ios;
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++)
{
int x=i-a[i];
mp[x]+=a[i];
if(mp[x]>ans) ans=mp[x];
}
cout<<ans;
return 0;
}
经验:
以后看到给的公式,先变形看看。
别他说啥是啥,跟着绕进去了。
1312C. Adding Powers(思维)
题意:
给定k,对于长度为n的0数组a,判断能否经过下列操作转化为数组b?
对于第 i 次操作(i 从 0 开始):
1.选择一个位置,将该位置上的值 += k^i
;
2.跳过该次操作。
可以在任意时刻停止操作。
思路:
也就是说:
对于目标数组中的每个数,都要能转化成k的幂次数之和。并且n个数的所需要的幂次不能重复。
所以,对于每个数,都要找到最大的k的幂次数(如果不是最大,那么对于这个数一定会有重复幂次)
遍历每个数,map记录幂次出现次数,如果一个幂次多次出现,那么就不能满足。
注意:log(x)函数对于大数有精度误差。
Code:
const int N = 200010, mod = 1e9+7;
int T, n, m, a[N];
int k,flag;
int p[N];
void pd(int x)
{
while(x)
{
int t;
for(int i=0;i<=x;i++){
if(pow(k,i)<=x) t=i;
else break;
}
if(mp[t]){
flag=1;return;
}
else mp[t]=1;
x-=pow(k,t);
}
}
signed main(){
Ios;
cin>>T;
while(T--)
{
flag=0;
cin>>n>>k;
mp.clear();
for(int i=1;i<=n;i++)
{
int x;cin>>x;
if(x) pd(x);
}
if(flag) cout<<"No\n";
else cout<<"Yes\n";
}
return 0;
}
289B. Polo the Penguin and Matrix(绝对值)
题意:
给定数k。对于n个数,每次操作可以挑一个数+k或者-k。
问,最少多少次操作,能够使得所有数相等?如果不能,输出-1。
思路:
对于一个数,加k或者减k对于其模k的值是不变的。
所以如果有两个数模k的值不同,那么这两个值无论如何操作都不能使其相等。
所以所有数模k的值都相等时,才有解。
如何得到最优解呢?
让操作次数最少,就是让|x-a1|/k+|x-a2|/k+|x-a3|/k+...+|x-an|/k = (|x-a1|+|x-a2|+|x-a3|+...+|x-an|)/k
的值最少,x为最终化为的相等的数。
那么,求|x-a1|+|x-a2|+|x-a3|+...+|x-an|
最小,之前见到过,x最优解为ai的中位数a[n/2+1]
。
Code:
const int N = 200010, mod = 1e9+7;
int T, n, m, a[N];
int main(){
Ios;
int k;
cin>>n>>m>>k;
int flag=0,cnt=0,t;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cnt++;
cin>>a[cnt];
if(cnt==1) t=a[1]%k;
else if(a[cnt]%k!=t) flag=1;
}
}
if(flag){
cout<<-1;return 0;
}
n*=m;
sort(a+1,a+n+1);
int x=a[n/2+1];
int ans=0;
for(int i=1;i<=n;i++)
{
ans+=abs(x-a[i])/k;
}
cout<<ans;
return 0;
}
扩展:
如果每次可以选择多个数+k或者-k呢?
如果在一个二维矩阵中,每次选择一个子矩阵+k或者-k呢?
1201C. Maximum Median(模拟)
题意:
给定一个长度为n的数列,给定数k。(n为奇数)
最多有k次操作,每次操作可以选一个位置,将该位置上的数+1。
问,数列中位数最多为多少?
思路:
因为是中位数,所以只有后面的n/2+1位对中位数有影响,所以要将k次操作都放在后面。
要让后面这n/2+1个位置中的最小值尽量大,将所有数放到优先队列,每次取队首进行操作。
但是k最大1e9,所以这个方案不可行。
这个题有个特性,每次操作只加1。
所以可以从小到大慢慢往上堆。比较后面的值比前面的值多多少,看能不能补上,包括前面所有数。
如果能补上,比较下一位;如果不能,看最多补多少。
补后的最小值便是答案。
Code:
signed main(){
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>b[i];
sort(b+1,b+n+1);
int cnt=0;
for(int i=n/2+1;i<=n;i++){
cnt++;
a[cnt]=b[i];
}
n=cnt;
int ans=0;
for(int i=1;i<n;i++)
{
if(m>=i*(a[i+1]-a[i])){
//开long long
m-=i*(a[i+1]-a[i]);
a[i]=a[i+1];
ans=a[i];
}
else
{
int x=m/i;
m=0;
ans=a[i]+x;
break;
}
}
if(m){
int x=m/n;
ans=a[n]+x;
}
cout<<ans;
return 0;
}