Simple 杂题练手记
Problem 1 世界上最可爱的珂朵莉###
时间限制:C/C++ 1秒,空间限制:C/C++ 65536K
题目描述
我永远喜欢珂朵莉~!
有两个长为n的序列a[i]与b[i]
你可以把任意不多于x个a序列中的数变成y
你可以把所有序列b中的数减去一个非负数t
你可以把a序列和b序列分别任意打乱
要求对于1 <= i <= n满足a[i] >= b[i]
求t的最小值
输入描述:
第一行三个数n,x,y
之后一行n个数表示a序列
之后一行n个数表示b序列
输出描述
一行一个非负数表示答案
输入示例1
输入
10 0 233333
227849 218610 5732 128584 21857 183426 199367 211615 91725 110029
8064826 14174520 10263202 9863592 592727 7376631 5733314 1062933 12458325 15046167
输出
14818318
数据范围
对于100%的数据,0 <= n <= 200000 , 0 <= x,y <= 2000000000,0<=a[i],b[i]<=2000000000
分析:
如果没有1操作将a序列中的数变成y,则改题只需要将a和b分别排序,然后找出对应位置的最大差值即可。
现在有操作1,则先将a序列排好序,然后从小到大将a序列中的值与y依次比较,如果小于y,就更换。再重新对变换后的a序列排序,将a序列和b序列依次比较,寻找相应位置上最大的差值即为所有答案!
#include<bits/stdc++.h>
using namespace std;
int A[200010], B[200010];
int main()
{
int n, x, y;
scanf("%d%d%d", &n, &x, &y);
for(int i = 0; i < n; i++)
scanf("%d", &A[i]);
for(int i = 0; i < n; i++)
scanf("%d", &B[i]);
sort(A, A+n);
sort(B, B+n);
for(int i = 0; i < n; i++)
{
if(x!=0 && A[i] < y)
{
A[i] = y, x--;
}
if(x <= 0 || A[i] > y) break;
}
sort(A, A+n);
int Ans = 0;
for(int i = 0; i < n; i++)
Ans = max(Ans, B[i]-A[i]);
printf("%d\n", Ans);
return 0;
}
电池的寿命###
总时间限制: 1000ms 内存限制: 65536kB
问题描述####
小S新买了一个掌上游戏机,这个游戏机由两节5号电池供电。为了保证能够长时间玩游戏,他买了很多5号电池,这些电池的生产商不同,质量也有差异,因而使用寿命也有所不同,有的能使用5个小时,有的可能就只能使用3个小时。显然如果他只有两个电池一个能用5小时一个能用3小时,那么他只能玩3个小时的游戏,有一个电池剩下的电量无法使用,但是如果他有更多的电池,就可以更加充分地利用它们,比如他有三个电池分别能用3、3、5小时,他可以先使用两节能用3个小时的电池,使用半个小时后再把其中一个换成能使用5个小时的电池,两个半小时后再把剩下的一节电池换成刚才换下的电池(那个电池还能用2.5个小时),这样总共就可以使用5.5个小时,没有一点浪费。
现在已知电池的数量和电池能够使用的时间,请你找一种方案使得使用时间尽可能的长。
输入格式####
输入包含多组数据。
输入第一行,一个整数T,表示数据的组数。
接下来每组数据包括两行,第一行是一个整数N (2 ≤ N ≤ 1000),表示电池的数目,第二行是N个正整数表示电池能使用的时间。
输出格式####
对每组数据输出一行,表示电池能使用的时间,保留到小数点后1位。
样例输入
2
2
3 5
3
3 3 5
样例输出
3.0
5.5
问题分析####
初看之下,本题感觉没有什么很好的思路,但应该有贪心的办法。
由于每枚电池的使用时间不同,而我们又要减少浪费才能使所有电池加起来用得最久,不难发现:如果我们把使用时间最长的电池比喻成第一战队,其他电池比喻成第二战队,使用时间就是战斗力,
每次从拿电池使用时间最长的和另外战队里电池使用时间最长的相互战斗消耗1个战斗力;并且在每次战斗后,重新调整战队的分配情况(最大电池容量可能发生了变化)。
事实上,你会发现:其实对于每一组数据只要判断最大的那个数是不是比其余的数的和都要大,如果成立的话那当然就是剩下的所有电池与最大的电池车轮战,最大值为n-1个数的和,如果不成立的话那么最大就是n个数的和的一半,也就是说电池是一定可以全部用完的。讲一下简单的证明过程,每次先对N个数进行排序,然后最大电池每次与其余电池PK一小时,如此进行下去最后必然是三种情况中的一种即(2 1 1)(1 1)(1 1 1),这三种情况都是可以用完的,所以电池必定会全部用完。
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n,T;
int buf[1000];
double ans;
cin>>T;
for(int t=1;t<=T;++t)
{
ans=0;
for(int i=0;i<n;i++){
scanf("%d", &buf[i]);
ans+=buf[i];
}
sort(buf,buf+n);
if(buf[n-1]>ans-buf[n-1])
ans=ans-buf[n-1];
else
ans=ans*1.0/2;
printf("%.1f\n", ans);
}
return 0;
}
CJOJ P1236 - 指数序列求和###
Description
求 \(1^b+2^b+…+a^b\) 的和除以10000的余数。
Input
第一行包含一个正整数N,表示有N组测试数据
接下来N行每行包含两个正整数a和b
Output
输出共N行,每行一个对应的答案
Sample Input
1
2 3
Sample Output
9
Hint
数据范围:
对于30%数据 \(n≤10,a,b≤1000\)
对于100%数据 \(n≤100,a,b≤10^9\)
分析
快速幂+同类余数优化
暴力分30分不用解析了,就算你用了快速幂,for循环一遍,还是30分。
for循环的时候循环枚举到a是一定不可能的,我们知道在我们取膜的时候 a*c%b =a%b*c%b,所以我们利用这个原理,因为是挨着快速乘,他们取膜以后的数也一定是连着的,分析之后就会发现ab和(a+mod)b对答案的影响是一样的,证明是显然的。
所以说我们对于任意 i^b 都可以写成 (i%mod)^b ,由于 mod 10000,模数只有一万可以推算出mod10000相同的数,分在一组,比如2333和23333和233333就分在一组。
然后,这一组有多少个数,我就把这个组的数快速幂求出b次方然后乘组中元素个数就行了(详情看代码)。
#include<bits/stdc++.h>
using namespace std;
int mod=10000;
int Pow(int x,int y)
{
int base=x, cnt=1;
while(y>0)
{
if(y%2==1)
cnt=cnt*base%mod;
base=base*base%mod;
y=y>>1;
}
return cnt;
}
int main()
{
int n,a,b,ans;
cin>>n;
while(n>0)
{
n=n-1, ans=0;
cin>>a>>b;
int k=min(mod-1,a);
for(int i=1;i<=k;++i)
//1~a范围内余数同为i的一共有 1+(a-i)/mod 组,统计余数相同的这组数的幂次方
ans=(ans+(1+(a-i)/mod)*Pow(i,b)%mod)%mod;
cout<<ans<<endl;
}
return 0;
}
化学反应###
Description####
有 N 种不同的物质,每种物质有两个属性——“能量”和“活度”。 N 种中的任意两种物质都可以发生反应;反应放热为两种物质的“能量”之差加一再乘上“活度”的较大值。
换句话说,设第 i 种物质的能量和活度分别为 Ai 和 Bi,则 i 和 j 反应的放热为 (| Ai-Aj |+1) * max(Bi, Bj)
现在你需要选出两种物质,最小化它们反应放出的热量。这个最小值是多少?
Input####
本题包含多组测试数据,第一行为测试数据组数 T。
对于每组数据:
第一行一个正整数 N,表示物质种类数。
接下来 N 行每行两个正整数 Ai、 Bi,表示第 i 种物质的“能量”和“活度”。
Output####
输出一行一个正整数,最小放热。 注意这个数可能需要 64 位整型来存储。
Sample Input####
1
7
19 5
5 6
1 2
8 4
25 10
12 3
9 6
Sample Output####
12
数据范围:####
对于 40%的数据,N<=1000,T<=5
对于另外 20%的数据,N<=10^5,Ai、 Bi<=10^3,T=1
对于 100%的数据,N<=10^5,Ai、 Bi<=10^9,T<=40
分析:
40%的数据 $ N<=1000,O(n^2)$ 的复杂度将任意两种物质进行两两反应,反应的最小值即为所求答案。复杂度 \(O(T*n^2)\)。
对于另外20%的数据,\(N<=10^5\),且 \(Ai Bi<=10^3,T=1\),可以发现物品的种类数很多,但是能量和活度的值域很小。如果我们对每种物质按照活度进行从小到大排序,本问题的关键在于找到 Bj<=Bi,且 | Aj-Ai |最小(即能量最接近i物质)的j物质。
由于 $ Bi<=10^3$,因此开vector值域活度的桶,将活度相同的物质丢进同一类桶中,从小到大排序后对于每一个Bi,查找B1~Bi的桶内所有的元素并让它们相互反应,最小的 | Ai-Aj |即为在两种物质活度最大值为Bi时反应的最小热量,
最后取整个反应里的最小值即为所求的答案。复杂度 \(O(T*10^3*n)\)。
对于100%的数据,$ Ai Bi<=10^9$ 重新回到本题的关键问题,在于对于活度为Bi的物质,要找到 Bj<=Bi,且 | Aj-Ai |最小(即能量最接近i物质)的j物质。
因此首先还是按照活度从小到大进行排序,然后根据排序后物质的活度 Bi 从小到大进行枚举,对于每一个 Bi 查找前面已经枚举完成的 j 物质(必定有Bj<=Bi),找到能量值恰好大于等于Ai的Aj,即为两种物质活度最大值为Bi时反应的最小热量,
接下来考虑如何查找 i 物质之前,能量值与 i 最接近的物质j,如果暴力去逐个比较,那么最坏的复杂度依然会达到 \(O(n^2)\)。最快的查找方法当然是二分查找了,要想使用二分查找那么i之前的物质能量属性 Aj 应当维护一个有序序列。
怎么维护物质i之前的能量属性Aj有序呢?
方法①: 插入排序
方法②: Set
最终时间复杂度 $ O(T*nlogn) $ ,Set常数巨大,很可能会被卡掉
#include <cstdio>
#include <iostream>
#include <set>
#include <algorithm>
using namespace std;
#define l_b lower_bound
#define w1 first
#define w2 second
#define m_p make_pair
#define LL long long
typedef pair<int,int> PII;
const int maxn=100005;
set<int> f;
set<int>::iterator lsh;
int T,N;
int a[maxn],b[maxn];
PII ls[maxn];
int main()
{
scanf("%d",&T);
for (int gb=1;gb<=T;gb++)
{
scanf("%d",&N);
for (int i=1;i<=N;i++) scanf("%d %d",&a[i],&b[i]);
for (int i=1;i<=N;i++) ls[i]=m_p(b[i],a[i]);
sort(ls+1,ls+N+1);
for (int i=1;i<=N;i++) a[i]=ls[i].w2,b[i]=ls[i].w1;
LL minc=1000000000;
minc=(LL)minc*minc;
f.clear();
for (int i=1;i<=N;i++)
{
if (i!=1) // i不是第一个,去寻找i前面活度Bj<=Bi,且能量最接近ai的物质j
{
lsh=f.l_b(a[i]); // 寻找i前面能量值第一个大于等于a[i]的位置
if (lsh!=f.end())
minc=min(minc,(LL)b[i]*((*lsh)-a[i]+1));
if (lsh!=f.begin()) // 寻找i前面能量值恰好小于a[i]的位置
{
--lsh;
minc=min(minc,(LL)b[i]*(a[i]-(*lsh)+1));
}
}
f.insert(a[i]);
}
cout<<minc<<endl;
}
return 0;
}