18.11.2紹一模擬賽
阿新 • • 發佈:2018-11-02
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) */