2020 牛客 NOIP 賽前集訓營-S(第一場)- C 牛牛的湊數遊戲
博主的 BiBi 時間
考場上做了好久這玩意兒。好不容易想出來 \(a_i\) 是 \(2\) 的非負整數次方的做法,不會敲。
嚶嚶嚶。
\(\text{Solution}\)
\(10\text{pts}\)
考場上真的有人和我一起打弱智十分嗎?
思路就是把所有情況枚舉出來。考場上非常智障竟然用了個 \(\text{map}\) 存不知道什麼的東西。其實只要大法師就行了。
#include <map> #include <cstdio> #include <algorithm> using namespace std; typedef long long ll; const int maxn=1e5+5; int n,m,a[maxn],tp; ll q[5000005]; map <ll,int> mp; int main() { int l,r,tmp; bool flag; scanf("%d %d",&n,&m); for(int i=1;i<=n;++i) scanf("%d",&a[i]); for(int i=1;i<=m;++i) { scanf("%d %d",&l,&r); mp.clear(); mp.insert(make_pair(0,1)); q[tp=1]=0; for(int j=l;j<=r;++j) { tmp=tp; for(int k=1;k<=tmp;++k) if(!mp.count(q[k]+a[j])) mp.insert(make_pair(q[k]+a[j],1)), q[++tp]=q[k]+a[j]; } sort(q+1,q+tp+1); flag=0; for(int i=1;i<=tp;++i) if(q[i]^(i-1)) {printf("%d\n",i-1); flag=1; break;} if(!flag) printf("%d\n",tp); } return 0; }
\(\text{20pts}\)
\(\mathcal O(n^2\log n)\)。
正解就是從這個而來的。考慮將原陣列排序然後依次加入,每次選小於等於當前 \(sum+1\) 的值加進 \(sum\) 一直加到加不了。
考慮先開始的 \(sum=0\) 即 \([0,sum]\) 之間所有數都可以被表示,此時我們可以加入一個新的數,可以使得 \([0+k,sum+k]\) 的所有數都可以被表示。不過我們要找到從 \(0\) 開始連續能被表示的區間,所以暫時將新擴充套件的區間的 \(l\) 至少要小於等於 \(sum+1\) 吧。所以我們選擇的下一個數的區間必須在 \([0,sum+1]\) 之間。
依次擴充套件後答案顯然就是 \(sum+1\)
#include <cstdio> #include <algorithm> using namespace std; typedef long long ll; const int maxn=1e5+5; int n,m,a[maxn],tp,b[maxn]; int main() { int l,r; ll tmp; scanf("%d %d",&n,&m); for(int i=1;i<=n;++i) scanf("%d",&a[i]); for(int i=1;i<=m;++i) { scanf("%d %d",&l,&r); for(int j=l;j<=r;++j) b[j-l+1]=a[j]; sort(b+1,b+r-l+2); tmp=0; for(int j=1;j<=r-l+1;++j) if(tmp+1>=b[j]) tmp+=b[j]; printf("%lld\n",tmp+1); } return 0; }
\(\text{100pts}\)
再接再厲!
咱們接著上文的思路,其實問題已經轉化為求小於等於一個數的數的和,還要可持久化(支援兩個版本相減),可以用可持久化 \(\text{Trie}\) 樹。
可以證明 \(ans\) 的增長只有 \(\log(1e9)\)。考慮上次的 \(ans\) 為 \(lastAns\),那麼這次選的數一定大於 \(lastAns\),不然會和上次選重,所以每次必須乘 \(2\)。
有一個應該注意的點:將 \(x\) 二進位制分解時因為原陣列範圍在 \(2^{30}\) 之間,但是新算出來的 \(ans\) 會爆,不能直接分解,應該轉化為 \(2^{31}-1\) 即 \(0-30\) 為全為 \(1\)。不然會有本來很大,但由於只算了 \(31\) 位算小的情況。
總時間複雜度 \(\mathcal O(n\log^2(1e9))\)。
#include <cstdio>
#define rep(i,_l,_r) for(register signed i=(_l),_end=(_r);i<=_end;++i)
#define fep(i,_l,_r) for(register signed i=(_l),_end=(_r);i>=_end;--i)
#define erep(i,u) for(signed i=head[u],v=to[i];i;i=nxt[i],v=to[i])
#define efep(i,u) for(signed i=Head[u],v=to[i];i;i=nxt[i],v=to[i])
#define print(x,y) write(x),putchar(y)
template <class T> inline T read(const T sample) {
T x=0; int f=1; char s;
while((s=getchar())>'9'||s<'0') if(s=='-') f=-1;
while(s>='0'&&s<='9') x=(x<<1)+(x<<3)+(s^48),s=getchar();
return x*f;
}
template <class T> inline void write(const T x) {
if(x<0) return (void) (putchar('-'),write(-x));
if(x>9) write(x/10);
putchar(x%10^48);
}
template <class T> inline T Max(const T x,const T y) {if(x>y) return x; return y;}
template <class T> inline T Min(const T x,const T y) {if(x<y) return x; return y;}
template <class T> inline T fab(const T x) {return x>0?x:-x;}
template <class T> inline T gcd(const T x,const T y) {return y?gcd(y,x%y):x;}
template <class T> inline T lcm(const T x,const T y) {return x/gcd(x,y)*y;}
template <class T> inline T Swap(T &x,T &y) {x^=y^=x^=y;}
typedef long long ll;
const int maxn=1e5+5;
int n,m,bin[33],siz,rt[maxn];
struct Tree {int lson,rson; ll val;} t[maxn*35];
void div(ll x) {
if(x>=(1ll<<31)-1) x=(1ll<<31)-1;
rep(i,0,30) bin[i]=((x>>i)&1);
}
void modify(int &o,int pre,int k,int dep) {
t[o=++siz]=t[pre];
if(dep<0) {t[o].val+=k; return;}
if(bin[dep]) modify(t[o].rson,t[pre].rson,k,dep-1);
else modify(t[o].lson,t[pre].lson,k,dep-1);
t[o].val=t[t[o].lson].val+t[t[o].rson].val;
}
ll query(int o,int dep) {
if(dep<0||o==0) return t[o].val;
if(bin[dep]) return t[t[o].lson].val+query(t[o].rson,dep-1);
return query(t[o].lson,dep-1);
}
int main() {
int x,l,r; ll ans,lastAns;
n=read(9),m=read(9);
rep(i,1,n) x=read(9),div(x),modify(rt[i],rt[i-1],x,30);
while(m--) {
l=read(9),r=read(9); lastAns=ans=0;
while(233) {
div(ans+1);
ans=query(rt[r],30)-query(rt[l-1],30);
if(lastAns==ans) {print(ans+1,'\n'); break;}
else lastAns=ans;
}
}
return 0;
}