1. 程式人生 > 實用技巧 >2020 牛客 NOIP 賽前集訓營-S(第一場)- C 牛牛的湊數遊戲

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;
}