CF895C: Square Subsets && 【BZOJ2844】albus就是要第一個出場
阿新 • • 發佈:2018-12-14
CF895C: Square Subsets && 【BZOJ2844】albus就是要第一個出場
這兩道題很類似,都是線性基的計數問題,解題的核心思想也一樣。
CF895C Square Subsets
題意
給定\(n\)個數,求多少種選數方案使得選出來的數乘積為完全平方數。\(n\leq 100000,a_i\leq70\)。
完全平方數的本質就是每個質因子的次數為偶數。
所以我們將每一個數唯一分解,然後記錄每個質因子的奇偶狀態,就得到了一個個01串。問題就變成了有多少個集合中的數異或為0。
我們建好線性基,設線性基的秩為\(m\),則答案就是\(2^{n-m}-1\)
因為線性基中的\(m\)個元素是線性無關的,並且可以表達出其他數的所有線性組合,所以其他\(2^{n-m}-1\)(減掉都不選的一種情況)的所有數都能線上性基中找到唯一的一個組合與之異或起來為0。
程式碼:
#include<bits/stdc++.h> #define ll long long using namespace std; int n; ll p[50],cnt; bool Insert(ll x) { for(int i=20;i>=0;i--) { if(!(x>>i)&1) continue ; if(!p[i]) return p[i]=x,1; x^=p[i]; } return 0; } ll pri[100]; bool vis[100]; void pre() { for(int i=2;i<=70;i++) { if(!vis[i]) pri[++pri[0]]=i; for(int j=1;j<=pri[0]&&i*pri[j]<=70;j++) { vis[i*pri[j]]=1; if(i%pri[j]==0) break; } } } void break_down(ll v) { ll x=0; for(int i=1;i<=pri[0];i++) { while(v%pri[i]==0) { x^=1<<i; v/=pri[i]; } } if(!Insert(x)) cnt++; } int main() { pre(); scanf("%d",&n); ll a; for(int i=1;i<=n;i++) { scanf("%lld",&a); break_down(a); } ll t=2,ans=1; for(;cnt;cnt>>=1,t=t*t%1000000007) { if(cnt&1)ans=ans*t%1000000007; } cout<<(ans-1+1000000007)%1000000007; return 0; }
【BZOJ2844】albus就是要第一個出場
我們就是要求比\(Q\)小的集合有多少個。
和上一道題一樣,我們就是要求比\(Q\)小,且能被線性基表示出來的數有多少個,我們設為\(lower\),則答案為\(lower\cdot 2^{n-m}+1\)。
問題其實就是求\(\leq Q-1\)的的最大的數的\(Rank\)。
我的實現方式不太一樣。我們就找\(\leq Q-1\)的數的數量。\(\leq Q-1\)一定是從高到低的前\(i-1\)位相同,第\(i\)位小一些,後面的幾位隨便。於是我們就考慮\(Q\)的前\(i\)位,我們設為\(Q'\),然後將所有數的前\(i\)
這樣做複雜度要比網上的一般寫法多一個\(log\),不過個人感覺好理解些。
程式碼:
#include<bits/stdc++.h>
#define ll long long
#define N 100005
#define mod 10086
using namespace std;
inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}
int n;
int p[35],a[N];
int Q;
bool Insert(int a) {
for(int i=30;i>=0;i--) {
if(a&(1<<i)) {
if(!p[i]) return p[i]=a,1;
else a^=p[i];
}
}
return 0;
}
int cnt;
void build(int k) {
cnt=0;
memset(p,0,sizeof(p));
for(int i=1;i<=n;i++) {
cnt+=Insert(a[i]>>k);
}
}
ll ans;
ll pw[N];
int main() {
n=Get();
pw[0]=1;
for(int i=1;i<=n;i++) pw[i]=(pw[i-1]<<1)%mod;
for(int i=1;i<=n;i++) a[i]=Get();
Q=Get();
for(int i=30;i>=0;i--) {
if(Q&(1<<i)) {
build(i);
if(!Insert((Q>>i)^1)) {
(ans+=pw[n-cnt])%=mod;
}
}
}
cout<<(ans+1)%mod;
return 0;
}