专栏文章
10.14 一中模拟高中2
个人记录参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @minlvl5y
- 此快照首次捕获于
- 2025/12/02 04:32 3 个月前
- 此快照最后确认于
- 2025/12/02 04:32 3 个月前
T1
给定 ,表示有 个数,每个数可以为 中的数,最大化
注意到这个式子的本质是将两两数字进行匹配,求出异或和。
对每一位进行考虑,也就是算出每一位的贡献。
注意到异或的性质,当一位的异或值是 时,一定是 ,所以每一位的贡献就是一的个数乘零的个数乘当前位置 的权值 。
注意到异或的性质,当一位的异或值是 时,一定是 ,所以每一位的贡献就是一的个数乘零的个数乘当前位置 的权值 。
所以我们将每个数字二进制分解,统计每一位 的个数(的个数可以用 计算),乘以权值即可,异或和就等于每一位的贡献之和。
这样我们将复杂度从 降到了 。
再来看到这道题,答案很显然,逐位考虑。要使和最大我们要让 和 的差值尽可能小(和为定值,差值越小乘积越大)。
取 时差值最小。而每一位都可以达到最大值,令 的数最高位是 ,其余为是 ,另一半数最高位是 ,其余位是 ,此时的答案为 。
CPP#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MOD = 1e9 + 7;
template<typename TY>
inline void read(TY &s){
ll x = 0,f = 1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
s = x * f;
}
ll n,m;
ll count(ll m){
ll cnt = 0;
while(m){
m/=2;
cnt++;
}
return cnt;
}
int main(){
freopen("testdata.in","r",stdin);
freopen("testdata.out","w",stdout);
int T;
read(T);
while(T--){
read(m); read(n);
if(m == 1){
cout << "0\n";
continue;
}
ll k = count(m);
k = 1ll << k;
k = (k - 1 + MOD) % MOD;
cout << ((n * n) / 4ll) % MOD * k % MOD << "\n";
}
return 0;
}
而异或还有许多美妙的性质,像可以进行前缀和运算。
计算区间异或和的和是要用这两种方法相结合,不同的是此时有 个数(前缀)。
例题
T2
有 台密码机,需要破解 台,每台有两个值 。如果破译时间达到 就破译成功,然后可以选择继续破译到 获得一个机械人偶,机械人偶拥有和你一样的效率,你们可以同时破译也可分开破译。特别地,如果一个密码机被破译后没有机械人偶则 ,否则 ,问破译 台密码机的最小时间。
第一步转化:发现人和机械人偶同时破译优于分开破译。
设破译速度为 ,破译 时间的密码机,显然 ,或者说这样操作至少不劣。所以人偶问题我们转化为每多一个人偶,破译的效率增加一倍。
第二步转化:如果我们已经定下来选几个密码机破译到 ,选几个密码机破译到 ,我们一定是先破译所有的 ,再破译所有的 ,且破译 的时候排序去做,因为破译 可以看作获得一样多的效率,把更多的效率攒到需要更长时间的时候去用,不能获得效率的 自然放在效率攒够后再做最优,问题转化为贪心问题。
策略有了,如何统计答案?
可以枚举破译了几个 (这样可以知道破译 时的效率),然后考虑到当 的时候, 太长的操作时间可能会影响到后续 的最小破译时间,比如这样。所以不是贪心的选最前面的几个 ,而是进行 记录前面破译了几个 ,几个 ,所用的最小时间。
可以枚举破译了几个 (这样可以知道破译 时的效率),然后考虑到当 的时候, 太长的操作时间可能会影响到后续 的最小破译时间,比如这样。所以不是贪心的选最前面的几个 ,而是进行 记录前面破译了几个 ,几个 ,所用的最小时间。
复杂度 ,考虑优化。
第三步转化:注意到关键信息,如果破译了最后一个 ,则前面没有啥都不破译的密码机,因为如果有,交换他们显然会让后面的破译效率更高。
所以我们不用记录前面破译了几个 ,当破译好了 个 后,后面几个 的贡献也可以用后缀的前缀和 计算,总复杂度 。
CPP所以我们不用记录前面破译了几个 ,当破译好了 个 后,后面几个 的贡献也可以用后缀的前缀和 计算,总复杂度 。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 510;
template<typename TY>
inline void read(TY &s){
ll x = 0,f = 1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
s = x * f;
}
int n,k;
struct node{
double a,b;
}z[N];
bool cmp1(node a,node b){
return a.a < b.a;
}
bool cmp2(node x,node y){
return x.b < y.b;
}
bool is_A,is_B;
void solve_is_A(){
sort(z+1,z+n+1,cmp1);
double res = 0;
for(int i=1;i<=k;i++){
res += z[i].a;
}
printf("%.10lf",res);
}
double c[N],s[N][N],f[N][N],ans;
void chkmin(double &x,double y){
if(x > y) x = y;
}
int main(){
// freopen("mechanic.in","r",stdin);
// freopen("mechanic.out","w",stdout);
read(n); read(k);
for(int i=1;i<=n;i++){
cin >> z[i].a >> z[i].b;
if(z[i].b != -1) is_A = true;
if(z[i].b != -1 && z[i].b != z[i].a) is_B = true;
}
if(!is_A){
solve_is_A();
return 0;
}
for(int i=1;i<=n;i++){
if(z[i].b == -1) z[i].b = 1e9;
}
for(int i=1;i<=n;i++){
for(int j=i;j<=n;j++){
c[j] = z[j].a;
}
sort(c+i,c+n+1);
for(int j=i;j<=n;j++){
s[i][j-i+1] = s[i][j-i] + c[j];
}
}
ans = s[1][k];
for(int u=0;u<=k;u++){
for(int i=0;i<=n;i++){
for(int j=0;j<=n;j++){
f[i][j] = 1e18;
}
}
f[0][0] = 0;
for(int i=1;i<=k;i++){
for(int j=0;j<i;j++){
chkmin(f[i][j],f[i-1][j] + z[i].a / (u + 1));
if(z[i].b < 1e9) chkmin(f[i][j+1],f[i-1][j] + z[i].b / (j + 1));
}
chkmin(ans,f[i][u] + s[i+1][k-i] / (u + 1));
// cerr << "dp: " << f[i][u] + s[i+1][k-i]/(u+1) <<"\n";
}
}
printf("%.10lf",ans);
return 0;
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...