T1 Alice 的幸运数
题意
给定n(n\le 100)个64位无符号整数(有顺序)。
我们可以对每个数取反,然后按顺序执行按位与\(nbsp或者\)nbsp按位或\(nbsp或者\)nbsp按位异或。
求最后结果的最小值。
分析
我们发现要使结果最小,高位尽量取0。
打个爆搜。。
发现n很大的时候全是0。
大胆猜想,无需证明!!!
if(n12){
printf("%d\n",0);
return ;
}
轻松AC。
代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#define ll long long
#define ull unsigned long long
#define file "lucky"
using namespace std;
ull read(){
char c;ull num,f=1;
while(c=getchar(),!isdigit(c))if(c=='-')f=-1;num=c-'0';
while(c=getchar(), isdigit(c))num=num*10+c-'0';
return f*num;
}
int n;
ull a[109],now,ans;
void dfs(int d){
if(d==n+1){
ans=min(ans,now);
return ;
}
ull tmp=now;
now=tmp&a[d];dfs(d+1);
if(ans==0)return ;
now=tmp&(~a[d]);dfs(d+1);
if(ans==0)return ;
now=tmp^a[d];dfs(d+1);
if(ans==0)return ;
now=tmp^(~a[d]);dfs(d+1);
if(ans==0)return ;
now=tmp|a[d];dfs(d+1);
if(ans==0)return ;
now=tmp|(~a[d]);dfs(d+1);
if(ans==0)return ;
}
void work(){
scanf("%d",&n);
if(n>12){
cout<<0<<endl;
return ;
}
ans=(1<<64)-1;
for(int i=1;i<=n;i++)a[i]=read();
now=a[1];dfs(2);
if(ans!=0){now=~a[1];dfs(2);}
cout<<ans<<endl;
}
int main()
{
freopen(file".in","r",stdin);
freopen(file".out","w",stdout);
int Case;
scanf("%d",&Case);
while(Case--)work();
return 0;
}
T2Marisa 的礼物
题意
一开始有1元钱,有\(n(n\le 10^5)\)个置换关系,当我们拥有超过\(r_i(r_i\le 10^9)\)元钱时可以花光所有钱,用\(t_i(t_i\le 10^9)\)的时间把身上的钱换成\(v_i(v_i\le 10^9)\)元。
问使身上的钱超过\(m(m\le 10^9)\),最少需要多少时间。
分析
很明显每个置换关系只可能用一遍。
我们可以跑背包。
但是背包的状态有点大。
发现其实\(n\)很小。
我们把容量离散化,这样就可以把容量缩到\(10^5\),可以跑背包。
\(f[v]\)表示当身上的钱为\(v\)的时候,最少时间。
\(f[v_i]=min{f[k],k\ge u_i}+t_i\)。
暴力求最小值。
然后我们就可以\(O(n^2)\)求出所有的价值了。
时间复杂度不够优。
我们继续考虑优化这个求最小值的过程。
单调队列?
并不是单调的。
线段树!
我们用线段树维护这个数组。
就可以\(O(nlogn)\)维护最小值了。
代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <bits/stdc++.h>
#define ll long long
#define file "gift"
using namespace std;
const int M=2e5+1000;
struct edge{
int u,v;
ll t;
}e[M];
int read(){
char c;int num,f=1;
while(c=getchar(),!isdigit(c))if(c=='-')f=-1;num=c-'0';
while(c=getchar(), isdigit(c))num=num*10+c-'0';
return f*num;
}
int n,m,tmp[M*3],cnt;
ll f[M*3];
ll tree[M*20];
bool cmp1(int a,int b){return a<b;}
bool cmp(edge a,edge b){return a.u<b.u;}
void update(int rt){
tree[rt]=min(tree[rt<<1],tree[rt<<1|1]);
}
void build(int l,int r,int rt){
if(l>r)return ;
if(l==r){tree[rt]=f[l];return;}
int mid=(l+r)>>1;
build(l,mid,rt<<1);
if(mid+1<=r)build(mid+1,r,rt<<1|1);
update(rt);
}
ll ask(int l,int r,int L,int R,int rt){
if(l<=L&&R<=r){return tree[rt];}
int mid=(L+R)>>1;
ll ans=1ll<<61;
if(mid>=l)ans=min(ans,ask(l,r,L,mid,rt<<1));
if(mid+1<=r)ans=min(ans,ask(l,r,mid+1,R,rt<<1|1));
return ans;
}
void change(int x,int L,int R,int rt,ll w){
if(L==R){tree[rt]=w;return ;}
int mid=(L+R)>>1;
if(mid>=x)change(x,L,mid,rt<<1,w);
else change(x,mid+1,R,rt<<1|1,w);
update(rt);
}
int fd(int x){
int l=1,r=cnt,mid;
while(l<=r){
mid=(l+r)>>1;
if(tmp[mid]==x)return mid;
if(tmp[mid]>x)r=mid-1;
else l=mid+1;
}
}
void work(){
cnt=0;n=read();tmp[++cnt]=m=read();
tmp[++cnt]=1;
for(int i=1;i<=n;i++){
tmp[++cnt]=e[i].v=read();
tmp[++cnt]=e[i].u=read();
e[i].t=read();
if(e[i].u>=e[i].v){
n--;i--;
continue;
}
}
sort(tmp+1,tmp+1+cnt,cmp1);
sort(e+1,e+1+n,cmp);
cnt=unique(tmp+1,tmp+1+cnt)-(tmp+1);
for(int i=1;i<=n;i++){
e[i].v=fd(e[i].v);
e[i].u=fd(e[i].u);
}
memset(f,0x3f,sizeof(f));
m=fd(m);f[1]=0;
build(1,cnt,1);
//cout<<tree[3]<<endl;
//cout<<ask(3,cnt,1,cnt,1)<<endl;
ll minn;
for(int i=1;i<=n;i++){
minn=(1<<31)-1;
minn=ask(e[i].u,cnt,1,cnt,1);
if(minn+e[i].t<f[e[i].v]){
f[e[i].v]=minn+e[i].t;
change(e[i].v,1,cnt,1,minn+e[i].t);
}
}
minn=(1<<31)-1;
minn=ask(m,cnt,1,cnt,1);
printf("%lld\n",((minn<(1ll<<61))?minn:-1));
}
int main()
{
freopen(file".in","r",stdin);
freopen(file".out","w",stdout);
int Case=read();
while(Case--)work();
return 0;
}
/* 发现每次换钱必须变得更多,那么先去除那些变少的边。
* 可以离散化。
* 我们对起点进行排序,然后升序跑一遍dp。
* 考虑线段树维护最小值,单点修改。
* f[v]=min{min{f[u]}+t};
* 时间复杂度
* 离散化nlogn,线段树logn。
* 循环n
* 总复杂度O(nlogn)
*/