统计方形
对于一个 n*m 的矩形,他所含的所有矩形的数量为 n ∗ m n*m n∗m,矩形数=正方形数 + 长方形数,对于正方形,我们枚举每个顶点作为右下方的顶点,数量为 m i n ( i , j ) min(i,j) min(i,j),然后长方形的数量就等于总数减去正方形的数量。
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
using ll = long long;
void solve()
{
ll n,m,ans1=0,ans2=0;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
ans1+=min(i,j); //正方形
ans2+=i*j-min(i,j); //长方形 = 总数 - 正方形
}
}
cout<<ans1<<" "<<ans2<<endl;
return ;
}
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int T; T=1;
while(T--) solve();
return 0;
}
子集枚举
选数
让每个子集对应一个二进制数,例如五个元素的集合,00000代表空集,从左到右为第 1 - 5 个元素。
非空子集的个数:
2
n
−
1
2^n-1
2n−1
构建只含某个输的集合:
1
<
<
(
i
−
1
)
1<<(i-1)
1<<(i−1)
并集:两个二进制数做或运算。
交集:两个二进制数做与运算。
包含:集合
A
1
A_1
A1 包含集合
A
2
A_2
A2 时,两个集合的交集为
A
2
A_2
A2 并集为
A
1
A_1
A1,即
(
A
1
∣
A
2
=
=
A
1
)
&
&
(
A
1
&
A
2
=
=
A
2
)
(A_1|A_2==A_1) \&\& (A_1 \& A_2==A2)
(A1∣A2==A1)&&(A1&A2==A2)
属于:判断某个元素是不是在集合中,是包含的特殊情况,先构造出只含这个元素的集合再用包含的方式进行判断即可。
补集:异或运算,同为0,不同为1。
函数:__builtin_popcount() 能返回一个数二进制下 1 的个数。
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
const int maxn = 23;
int a[maxn];
bool check(int x)
{
for(int i=2;i<=x/i;i++)
if(x%i==0) return false;
return true;
}
void solve()
{
int n,k; cin>>n>>k;
int ans=0;
for(int i=1;i<=n;i++) cin>>a[i];
int u=1<<n; // u-1就是全集
for(int s=0;s<u;s++) //枚举子集
{
if(__builtin_popcount(s)==k) //找到 k 元子集
{
int sum=0;
for(int i=1;i<=n;i++)
if(s&(1<<i-1)) sum+=a[i];
if(check(sum)) ans++;
}
}
cout<<ans<<endl;
return ;
}
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int T; T=1;
while(T--) solve();
return 0;
}
组合的输出
正着枚举无法保证次序,一种可行的方案是倒着枚举,往里面存 0 到 n-1,然后再倒序输出 n - a[i],具体看代码。
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
const int maxn = 23;
int a[maxn];
void solve()
{
int n,r; cin>>n>>r;
int u=1<<n;
for(int s=u;s>=0;s--)
{
int cnt=0;
for(int i=0;i<n;i++)
if(s&(1<<i)) a[++cnt]=i;
if(cnt==r)
{
for(int i=cnt;i>=1;i--)
cout<<setw(3)<<n-a[i];
cout<<endl;
}
}
return ;
}
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int T; T=1;
while(T--) solve();
return 0;
}
- next_permutation() 函数用于生成容器或序列的下一个字典序排列。在 algorithm 库中。
bool next_permutation(BidirectionalIterator first, BidirectionalIterator last, Compare comp);
// 起始迭代器,自定义顺序,默认升序
// 成功生成返回 true
// 如果当前排列已经是字典序的最后一个排列,则返回 false,并生成字典序的第一个排列(即最小排列)
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
const int maxn = 13;
int a[maxn];
void solve()
{
int n; cin>>n;
for(int i=1;i<=n;i++)
a[i]=i;
do
{
for(int i=1;i<=n;i++)
cout<<setw(5)<<a[i];
cout<<endl;
} while (next_permutation(a+1,a+1+n));
return ;
}
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int T; T=1;
while(T--) solve();
return 0;
}
由于强制用四根木棒,因此一定是两根相同的加上另外两根。观察到 a i a_i ai 在 5000 以内,因此开一个桶来记录每个长度的木棒的数量。当另外两根木棒的长度不同时,对答案的贡献为 C f [ i ] 2 ∗ f [ j ] ∗ f [ i − j ] C_{f[i]}^{2}*f[j]*f[i-j] Cf[i]2∗f[j]∗f[i−j] 当另外两根木棒长度也相同时,对答案的贡献为 C f [ i ] 2 ∗ C f [ j ] 2 C_{f[i]}^{2}*C_{f[j]}^{2} Cf[i]2∗Cf[j]2。
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
const int maxn = 1e5+3;
const int mod = 1e9+7;
int a[maxn],n,ans;
int f[maxn]; // 桶
void solve()
{
cin>>n;
int minn=1e9,maxn=-1e9;
for(int i=1;i<=n;i++)
{
cin>>a[i],f[a[i]]++;
minn=min(minn,a[i]);
maxn=max(maxn,a[i]);
}
for(int i=minn+1;i<=maxn;i++) //由于必须选四根,因此从 minn+1 开始
{
if(f[i]<=1) continue; // 不可能组成三角形
for(int j=minn;j<=i/2;j++)
{
if(f[j]&&f[i-j]) // 能组成三角形
{
if(j!=i-j) // 两数不同
ans+=((f[i]*(f[i]-1)/2)%mod)*(f[j]*f[i-j]%mod)%mod;
else if(f[j]>=2) // 两数相同
ans+=(((f[i]*(f[i]-1))/2)%mod)*((f[j]*(f[j]-1)/2)%mod)%mod;
}
}
}
cout<<ans%mod<<endl;
}
signed main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int T; T=1;
while(T--) solve();
return 0;
}
```