并查集
合根植物(2017 国赛)
}
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6;
// 查找元素x的根节点(即所在集合的代表元素)
int find(int x)
{
if (x != p[x]) // 如果x不是自己的根节点
{
p[x] = find(p[x]); // 路径压缩,将x的父节点直接指向根节点
}
return p[x]; // 返回根节点
}
int main()
{
int m, n, k;
cin >> m >> n >> k; // 输入矩阵的行数m、列数n和要进行的合并操作次数k
// 初始化并查集,每个元素都是自己的根节点
for (int i = 1; i <= m * n; i++)
{
p[i] = i;
}
// 执行k次合并操作
while (k--)
{
int a, b;
cin >> a >> b; // 输入要合并的两个元素的编号a和b
// 合并a和b所在的集合
// 先找到a和b的根节点,然后将a的根节点的父节点指向b的根节点
p[find(a)] = find(b);
}
// 统计最终的集合数量(即根节点的数量)
int res = 0;
for (int i = 1; i <= m * n; i++)
{
// 如果p[i]等于i,说明i是根节点
res += p[i] == i;
}
cout << res; // 输出最终的集合数量
return 0;
}
求组合数(动态规划)可用于取模操作
模板
#include<bits/stdc++.h>
using namespace std;
vector <int> t[100];
int main()
{
//C(b,a)
int a,b;
cin>>b>>a;
int shang=a;
int cha=b-a;
for(int i=0;i<=cha+1;i++)
for(int j=0;j<=shang;j++){
int s=0;
if(i)s+=t[i-1][j];
if(j)s+=t[i][j-1];
t[i].push_back(i||j?s:1);
}
printf("%d",t[cha][shang]);
return 0;
}
(洛谷 P8848)
#include<bits/stdc++.h>using namespace std;
#define int long long
const int N=10086;
const int mol=998244353;
int n,cp=0,cq=0;vector<int> a[N];
signed main(){
int x;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&x),x==1?cp++:cq++;
int m=cp-cq;
a[0].push_back(1);
if(m>0){
for(int i=0;i<=cq;i++)
for(int j=0;j<=m;j++){
int s=0;
if(i&&j!=m)s+=a[i-1][j+1];
if(j)s+=a[i][j-1];
a[i].push_back(i||j?s%mol:1);
}
printf("%d",a[cq][m]);
}
else{
for(int i=0;i<=1-m;i++)
for(int j=0;j<=cp;j++){
int s=0;
if(i)s+=a[i-1][j];
if(j)s+=a[i][j-1];
a[i].push_back(i||j?s%mol:1);
}
printf("%d",a[1-m][cp]);
}
return 0;
}
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=10086;
const int mol=998244353;
// 其中a[i][j]表示有i个-1和j个1时,前缀和为某个特定值(取决于i和j)的序列数量
int n,cp=0,cq=0;
vector<int> a[N];
signed main(){
int x;
scanf("%d",&n); // 读取元素数量n
// 读取每个元素的值,并统计1和-1(或0)的数量
for(int i=1;i<=n;i++)
scanf("%d",&x), x==1?cp++:cq++;
// 这里m是1的数量减去-1的数量
int m=cp-cq;
a[0].push_back(1);
// 如果m大于0(即1的数量多于-1的数量)
if(m>0){
// 通过DP计算a[i][j]的值
for(int i=0;i<=cq;i++) // -1的数量从0到cq
for(int j=0;j<=m;j++){ // 1的数量从0到m
int s=0;
// 如果i不为0且j不等于m,则从a[i-1][j+1]转移(即添加一个-1)
if(i&&j!=m)s+=a[i-1][j+1];
// 如果j不为0,则从a[i][j-1]转移(即添加一个1)
if(j)s+=a[i][j-1];
// 更新a[i][j]的值,注意取模
a[i].push_back(i||j?s%mol:1);
}
// 输出结果,即a[cq][m],表示有cq个-1和m个1时,前缀和为m的序列数量
printf("%d",a[cq][m]);
}
// 如果m小于等于0(即-1的数量多于或等于1的数量)
//直接在-1里面插孔放1即可,排列组合
else{
for(int i=0;i<=1-m;i++)
for(int j=0;j<=cp;j++){
int s=0;
if(i)s+=a[i-1][j];
if(j)s+=a[i][j-1];
a[i].push_back(i||j?s%mol:1);
}
printf("%d",a[1-m][cp]);
}
return 0;
}
树状数组
小朋友排队(蓝桥)
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long ll;
const int N=1e6+7;
int h[N],cnt[N],arr[N];
//找左边比他大,右边比他小的一共多少个
int lowbit(int x)
{
return x&(-x);
}
void add(int pos)
{
for(int i=pos;i<N;i+=lowbit(i))
arr[i]++;
}
int presum(int idx)
{
int res=0;
for(int i=idx;i;i-=lowbit(i))
res+=arr[i];
return res;
}
int main()
{
int n;cin>>n;
for(int i=1;i<=n;i++)
{
cin>>h[i],h[i]++;
}
//从后往前找比他小的
for(int i=n;i>0;i--)
{
cnt[i]+=presum(h[i]-1);
//presum(h[i])身高小于等于和h[i]的个数
add(h[i]);
}
memset(arr,0,sizeof arr);
//找比他大的,总的人数减去presum(h[i])
for(int i=1;i<=n;i++)
{
cnt[i]+=presum(N)-presum(h[i]);
add(h[i]);
}
ll res=0;
for(int i=1;i<=n;i++)
{
res+=(ll)cnt[i]*(cnt[i]+1)/2;
}
cout<<res;
return 0;
}
逆序对(当元素很大的时候处理方法,防止RE)
给的数列元素很大,直接对原序列处理会导致RE,因此可以用原序列的相对大小顺序求逆序对。
即将原先的数列排序后,用对他们的原始下标求逆序对。
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long ll;
const int N = 5e5 + 7;
int arr[N],ranks[N];
struct node
{
int num,val;
}a[N];
int cmp(node a,node b)
{
if(a.val==b.val)
return a.num<b.num;
else
return a.val<b.val;
}
// 树状数组中的lowbit函数
int lowbit(int x) {
return x & (-x);
}
// 在树状数组的pos位置增加1
void add(int pos) {
for (int i = pos; i < N; i += lowbit(i))
arr[i]++;
}
// 计算树状数组中从1到idx的前缀和
int presum(int idx) {
ll res = 0;
for (int i = idx; i; i -= lowbit(i))
res += arr[i];
return res;
}
int main() {
int n;
scanf("%d",&n);
for(int i = 1; i <= n; i++) {
scanf("%d",&a[i].val),a[i].num=i;
}
//对排序后数的下标求逆序对,等于对原数列求逆序对
sort(a+1,a+n+1,cmp);
//将排序后的下标序作为要求,逆序对的数列,标上他们的下标
for(int i=1;i<=n;i++)
ranks[a[i].num]=i;
ll res=0;
//从前往后找比他大的,总的人数减去presum(ranks[i])
for (int i = 1; i <= n; i ++) {
res += presum(N)-presum(ranks[i]);
add(ranks[i]);
}
//也可以这样做 当前个数减去 presum(ranks[i]) 只不过要先添加元素
// for (int i = 1; i <= n; i ++) {
// add(ranks[i]);
// res += i-presum(ranks[i]);
// }
printf("%lld",res);
return 0;
}
双序列逆序对(P1966)
//以给定排序顺序,进行树状数组求逆序对
// 1 3 4 2 1 7 2 4 原序列
// 1 4 2 3 1 3 4 2 排序后下标序列
// ranks[1]=1,ranks[4]=3,ranks[2]=4,ranks[3]=2
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long ll;
const int N = 1e5 + 7;
int arr[N],ranks[N];
ll mol=1e8-3;
struct node
{
ll num,val;
}a[N],b[N];
int cmp(node a,node b)
{
if(a.val==b.val)
return a.num<b.num;
else
return a.val<b.val;
}
// 树状数组中的lowbit函数
int lowbit(int x) {
return x & (-x);
}
// 在树状数组的pos位置增加1
void add(int pos) {
for (int i = pos; i < N; i += lowbit(i))
arr[i]++;
}
// 计算树状数组中从1到idx的前缀和
int presum(int idx) {
ll res = 0;
for (int i = idx; i; i -= lowbit(i))
res += arr[i];
return res;
}
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i].val,a[i].num=i;
}
for(int i=1;i<=n;i++)
{
cin>>b[i].val,b[i].num=i;
}
sort(a+1,a+1+n,cmp);
sort(b+1,b+1+n,cmp);
for(int i=1;i<=n;i++)
{
ranks[a[i].num]=b[i].num;
}
ll res=0;
for(int i=1;i<=n;i++)
{
add(ranks[i]);
res=(res+i-presum(ranks[i]))%mol;
}
cout<<res;
return 0;
}
线段树
(洛谷P4588)
线段树
#include<bits/stdc++.h>
using namespace std;
long long sum[10000001];
long long mod;
//每次更新冲下往上
void update(int now)
{
//父节点为两子节点的积,根据题目
sum[now]=(sum[now*2]*sum[now*2+1])%mod;//时刻mod
}
//往下遍历到叶子在往上求每一个父节点
void build(int now,int l,int r)
{
if(l==r)
{
sum[now]=1;//建树叶子初始值为1,根据题目
return ;
}
int mid=(l+r)/2;
build(now*2,l,mid);//往左递归
build(now*2+1,mid+1,r);//往右递归
update(now);
}
//当前位置,总的左,总的右,需要修改的左,需要修改的右,需要修改的值
void change(int now,int l,int r,int lgo,int rgo,int nm)//套的区间修改模板
{
if(l>=lgo&&r<=rgo)
{
sum[now]=nm;
return ;
}
int mid=(l+r)/2;
if(lgo<=mid)
change(now*2,l,mid,lgo,rgo,nm);
if(rgo>mid)
change(now*2+1,mid+1,r,lgo,rgo,nm);
update(now);
}
int main()
{
int t;
cin>>t;
for(int i=1;i<=t;i++)
{
int q,op,m;
cin>>q>>mod;
build(1,1,q);
for(int i=1;i<=q;i++)
{
cin>>op>>m;
if(op==1)
{
change(1,1,q,i,i,m);
sum[1]%=mod;
}
else
change(1,1,q,m,m,1);
cout<<sum[1]%mod<<endl;//输出树根
}
}
return 0;
}