Noip模擬96 2021.11.13
T1 子集和
看出是一道揹包的切得都飛快,我沒用揹包於是做了很長時間。。。
因為程式碼太難調了!!!!
建議不要看我的,不過如果你想整活兒學一些\(nb\)的演算法可以看一看
我拿一個指標從\(m-1\)開始往前掃,發現越到後面的陣列成他用的集合的大小就越大
那麼從後往前掃,定義指標\(p\)為當前掃到的桶不為空的元素,那麼我們將會把\(m-p\)放入答案中
他一定會存在於\(a\)陣列中,且\(p\)就是除了這個數的剩下的數的加和
這樣我們每找到一個不空的桶就給他更新一個答案,同時對當前的\(a\)陣列中所有元素能夠組成的數進行統計
把能夠組成的數的桶該減的減掉
做法就是拿一個\(set\)
其實不難發現,如果程式碼寫的對的話,\(set\)中最後存的所有元素就是\(b\)陣列的下標以及其對應的桶的個數
subset(Very disgusting!!)
#include<bits/stdc++.h> #define int long long using namespace std; FILE *wsn; auto read=[](){ int x=0,f=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();} while(ch<='9'&&ch>='0'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*f; }; const int NN=10001; int n,m,b[NN],a[NN],top,pot,c[NN]; struct node{ int val; mutable int cnt; bool operator<(const node&x)const{ return val<x.val; } }; set<node>S; node stk[NN],skt[NN]; inline void push(int x){ for(auto s:S) if(S.find(node{x+s.val,0})==S.end()) stk[++top]=node{x+s.val,s.cnt}; else skt[++pot]=node{x+s.val,s.cnt}; while(pot) S.find(node{skt[pot].val,0})->cnt+=skt[pot].cnt,--pot; while(top) S.insert(node{stk[top].val,stk[top].cnt}),--top; if(S.find(node{x,0})==S.end()) S.insert(node{x,1}); else S.find(node{x,0})->cnt++; for(auto s:S) c[s.val]=b[s.val]-s.cnt; } namespace WSN{ inline short main(){ wsn=freopen("subset.in","r",stdin); wsn=freopen("subset.out","w",stdout); n=read(); m=read(); for(int i=0;i<=m;i++)c[i]=b[i]=read(); int p=m-1,tmp=0,tim=0; while(tmp<n-1){ while(!c[p]&&p) --p; if(!c[m-p]) c[p]=0; else --c[p],a[++tmp]=m-p,push(m-p); }int sm=0; for(int i=1;i<n;i++)sm+=a[i],printf("%lld ",a[i]);printf("%lld\n",m-sm); return 0; } } signed main(){return WSN::main();}
T2 異或
發現考場上打二維偏序的好像又只有我一個。。。。
好的剛剛發現了一個,sdfz兄弟很棒棒!!!
然而非常不服,發現常數小的\(O(n^3)\)居然也能拿\(40\)!!!很氣憤
不懂為啥能跑過\(8e9\)
正解是\(trie\)數思想或者說是跟二進位制位有關的,顯然。。
考慮\(i,k\)的每一位,發現他們的大小決策於從低到高的第一個不同的位上
那麼我們列舉每一位,設列舉的是第\(p\)位,再列舉從\(1\)到\(n\)找到每一位上的數是啥,記一下高於第\(p\)位的數為\(res\),並進行離散化
那麼我們發現如果\(a_k>a_i\),有以下幾種情況
- \(a_k[p]=1,a_i[p]=0,a_j[p]=0\)
- \(a_k[p]=0,a_i[p]=1,a_j[p]=1\)
那麼我們列舉的\([1,n]\)相當於是在列舉\(a_k[p]\),
那麼我們只需要知道字首的\(0/1\)個數\(sum[0/1]\)
和當前的\(k\)的\(res\)相同的\(i\)的字首的\(0/1\)的個數\(cnt[res][0/1]\)
以及需要容斥掉的算重的個數\(pre[res][0/1]\)即可在\(O(1)\)複雜度內計算答案
xor
#include<bits/stdc++.h>
#define int long long
using namespace std; FILE *wsn;
auto read=[](){
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch<='9'&&ch>='0'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
};
const int NN=5e5+5;
int n,a[NN],ans,id;
int sum[2],cnt[NN][2],pre[NN][2];
unordered_map<int,int>mp;
namespace WSN{
inline short main(){
wsn=freopen("xor.in","r",stdin);
wsn=freopen("xor.out","w",stdout);
n=read();for(int i=1;i<=n;i++)a[i]=read();
for(int i=30;i;i--){
sum[0]=sum[1]=0; id=0; mp.clear();
memset(cnt,0,sizeof(cnt));
memset(pre,0,sizeof(pre));
for(int j=1;j<=n;j++){
int it=a[j]>>i-1&1,res=a[j]>>i;
// cout<<it<<endl;
if(!mp[res])mp[res]=++id;
res=mp[res];
ans+=sum[it^1]*cnt[res][it^1]-pre[res][it^1];
++sum[it]; ++cnt[res][it]; pre[res][it]+=sum[it];
}
}
printf("%lld\n",ans);
return 0;
}
}
signed main(){return WSN::main();}
T3 異或2
珍愛生命,遠離高精
關於遞推式,我只想說第一次看見要推異或的式子的。。。
比較明顯是要按照奇偶分類討論的,剩下的直接看這個吧,感覺我寫出來也是一樣的,看著式子就比較好理解了
主要思想就是看每個數字的最後一位上是\(0/1\)對答案的貢獻
rox
#include<bits/stdc++.h>
#define int long long
using namespace std; FILE *wsn;
namespace BIG_INT{
const int base=1e16;
typedef __uint128_t ULL;
struct Big_Int{
int p[100];
ULL has;
Big_Int(){}
Big_Int(int x){memset(p,0,sizeof(p));p[0]=1;p[1]=x;}
inline void gethash(){has=p[0];for(int i=1;i<=p[0];i++)has*=p[i];}
inline Big_Int READ(){
Big_Int ret=Big_Int(0); char s[505];
scanf("%s",s+1); int len=strlen(s+1);
for(int i=1;i<=len;i++)ret=ret*10+Big_Int(s[i]-'0');
return ret;
}
inline void print(){
printf("%lld",p[p[0]]);
for(int i=p[0]-1;i>0;--i) printf("%016lld",p[i]);
puts("");
}
Big_Int operator*(const int&x)const{
Big_Int ret=Big_Int(0);int add=0; ret.p[0]=p[0];
for(int i=1;i<=ret.p[0];++i){
ret.p[i]=p[i]*x+add;
add=ret.p[i]/base;
ret.p[i]-=add*base;
}
if(add) ret.p[++ret.p[0]]=add;
ret.gethash();
return ret;
}
Big_Int operator+(const Big_Int&a)const{
Big_Int ret=Big_Int(0);int add=0; ret.p[0]=max(a.p[0],p[0]);
for(int i=1;i<=ret.p[0];++i){
ret.p[i]=a.p[i]+p[i]+add;
add=ret.p[i]/base;
if(ret.p[i]>=base) ret.p[i]-=base;
}
if(add) ret.p[++ret.p[0]]=add;
ret.gethash();
return ret;
}
Big_Int operator-(const int&x)const{
Big_Int ret=Big_Int(0); ret.p[0]=p[0];
for(int i=1;i<=ret.p[0];i++)ret.p[i]=p[i];
ret.p[1]-=x; int pos=1;
while(ret.p[pos]<0){
ret.p[pos]+=base;
++pos; ret.p[pos]--;
}
if(!ret.p[ret.p[0]]) ret.p[0]--;
ret.gethash();
return ret;
}
Big_Int operator/(const int&x)const{
Big_Int ret=Big_Int(0); ret.p[0]=p[0];
for(int i=1;i<=ret.p[0];i++)ret.p[i]=p[i];
if(ret.p[1]&1) ret.p[1]--;
for(int i=ret.p[0];i;i--){
if(ret.p[i]&1) ret.p[i-1]+=base;
ret.p[i]/=x;
}
if(!ret.p[p[0]]) --ret.p[0];
ret.gethash();
return ret;
}
};
map<ULL,Big_Int> has;
}using namespace BIG_INT;
inline Big_Int dfs(Big_Int x){
if(x.p[0]<=1&&x.p[1]<=0) return has[x.has]=Big_Int(0);
if(has.find(x.has)!=has.end()) return has[x.has];
Big_Int k=x/2;
if(x.p[1]&1) has[x.has]=dfs(k)*4+k*6;
else has[x.has]=dfs(k)*2+dfs(k-1)*2+k*4-4;
return has[x.has];
}
Big_Int n,m,ans;
namespace WSN{
inline short main(){
wsn=freopen("rox.in","r",stdin);
wsn=freopen("rox.out","w",stdout);
n=n.READ();
dfs(n).print();
return 0;
}
}
signed main(){return WSN::main();}