2024寒假小结
树
【luogu P1084】 [NOIP2012 提高组] 疫情控制
题目描述
H 国有
H 国的首都爆发了一种危害性极高的传染病。当局为了控制疫情,不让疫情扩散到边境城市(叶子节点所表示的城市),决定动用军队在一些城市建立检查点,使得从首都到边境城市的每一条路径上都至少有一个检查点,边境城市也可以建立检查点。但特别要注意的是,首都是不能建立检查点的。
现在,在 H 国的一些城市中已经驻扎有军队,且一个城市可以驻扎多个军队。一支军队可以在有道路连接的城市间移动,并在除首都以外的任意一个城市建立检查点,且只能在一个城市建立检查点。一支军队经过一条道路从一个城市移动到另一个城市所需要的时间等于道路的长度(单位:小时)。
请问最少需要多少个小时才能控制疫情。注意:不同的军队可以同时移动。(
解题思路
军队可以同时移动,求最少时间,让我们想到二分。
已知时间,我们如何求是否存在可行一种可行的方案?我们可以贪心解决。
首先,先将所有的军队尽量上移到根节点的子节点,将所有能到达根节点的子节点的军队按剩余时间从小到大排。
然后处理哪些根节点的子节点的子树还是有未符合条件的(不包含已到根节点的子节点的军队),很明显,对于哪些未符合条件的,只需将军队移动到根节点的子节点就可以了。
找哪些军队可以去别的子树,若军队所在子树不符合条件,那么也需要留下一个不能走到根节点到走回来的节点(贪心,该军队从根节点走到的节点所需时间必小于它走到根节点的时间,不如让其他点驻扎),处理好军队统一到根节点,排序后与不满足要求的子树比较即可。
时间复杂度
Code
#include<bits/stdc++.h>
using namespace std;
long long n,m,d[50005],f[50005][21],f1[50005][21],b[50005],ll,tr[50005],rr,fa[50005],bb[50005],l1;
vector<long long> a[50005],t[50005];
bool v[50005];
void dijah(long long x,long long y)
{
fa[x]=fa[y];
if(y==1)fa[x]=x;
for(int i=0;i<a[x].size();i++)
{
if(a[x][i]==y)continue;
dijah(a[x][i],x);
f1[a[x][i]][0]=t[x][i];
f[a[x][i]][0]=x;
}
return;
}
bool dfs(long long x,long long y)
{
if(v[x])return true;
if(a[x].size()==1&&a[x][0]==y)
{
if(y==1)tr[++rr]=x;
return false;
}
bool qwe=true;
for(int i=0;i<a[x].size();i++)
{
if(a[x][i]==y)continue;
qwe&=dfs(a[x][i],x);
}
if((!qwe)&&(y==1))tr[++rr]=x;
return qwe;
}
bool cmp1(long long q,long long w)
{
if(!v[q])return false;
if(!v[w])return true;
return f1[q][0]>f1[w][0];
}
bool cmp2(long long q,long long w)
{
return q>w;
}
bool cmp3(long long q,long long w)
{
return f1[q][20]>f1[w][20];
}
int main()
{
long long x,y,z;
scanf("%lld",&n);
for(int i=1;i<n;i++)
{
scanf("%lld%lld%lld",&x,&y,&z);
a[x].push_back(y);
a[y].push_back(x);
t[x].push_back(z);
t[y].push_back(z);
}
dijah(1,0);
for(int i=1;i<=20;i++)
{
for(int j=1;j<=n;j++)f[j][i]=f[f[j][i-1]][i-1],f1[j][i]=f1[j][i-1]+f1[f[j][i-1]][i-1];
}
scanf("%lld",&m);
for(int i=1;i<=m;i++)
{
scanf("%lld",&d[i]);
}
long long l=1,r=1e10+5,mid,p,poi,s=1e10+5;
while(l<=r)
{
memset(v,false,sizeof(v));
mid=(l+r)>>1;
ll=rr=l1=0;
for(int i=1;i<=m;i++)
{
p=d[i];
poi=mid;
for(int j=20;j>=0;j--)
{
if(f1[p][j]<=poi)
{
poi-=f1[p][j];
p=f[p][j];
}
}
if(p<=1)b[++ll]=d[i];
else
{
v[p]=true;
}
}
dfs(1,0);
if(rr>ll)
{
l=mid+1;
continue;
}
memset(v,false,sizeof(v));
for(int i=1;i<=rr;i++)v[tr[i]]=true;
sort(b+1,b+ll+1,cmp3);
for(int i=1;i<=ll;i++)
{
if(v[fa[b[i]]]&&f1[b[i]][20]+f1[fa[b[i]]][20]>mid)v[fa[b[i]]]=false;
else bb[++l1]=mid-f1[b[i]][20];
}
p=0;
sort(tr+1,tr+rr+1,cmp1);
sort(bb+1,bb+l1+1,cmp2);
for(int i=1;i<=rr;i++)
{
if(!v[tr[i]])continue;
if(bb[i]-f1[tr[i]][0]<0)
{
p=1;
break;
}
}
if(p)
{
l=mid+1;
continue;
}
r=mid-1;
s=min(s,mid);
}
if(s==1e9+5)cout<<-1;
else cout<<s;
return 0;
}
【CF1132G】 Greedy Subsequences
题目描述
定义一个序列的最长贪心严格上升子序列为:若选出的子序列为
给定一个长度为
其中
解题思路
因为每个数后面接的肯定是第一个比他大的数(只有一个),所以我们可以以此建一棵树,每个节点的父亲为他后面对应的第一个比它大的数。
此问题转化为:树上一个点集,每次加入一个节点并删去一个节点,求一个节点距离祖先最大的距离。
我们可以用
用线段树维护,时间复杂度
Code
#include<bits/stdc++.h>
using namespace std;
long long n,m,a[1000005],out[1000005],dfn[1000005],num,f[4000005],d[4000005];
stack<long long> l;
vector<long long> t[1000005];
void galaxy(long long x,long long l,long long r,long long v)
{
f[x]+=v,d[x]+=v;
return;
}
void pushdown(long long x,long long l,long long r)
{
if(d[x]==0)return;
long long mid=(l+r)>>1,lc=x<<1,rc=(x<<1)|1;
galaxy(lc,l,mid,d[x]),galaxy(rc,mid+1,r,d[x]);
d[x]=0;
return;
}
void dijah(long long x,long long l,long long r,long long ql,long long qr,long long v)
{
if(ql<=l&&r<=qr)
{
galaxy(x,l,r,v);
return;
}
pushdown(x,l,r);
long long mid=(l+r)>>1,lc=(x<<1),rc=(x<<1)|1;
if(ql<=mid)dijah(lc,l,mid,ql,qr,v);
if(qr>mid)dijah(rc,mid+1,r,ql,qr,v);
f[x]=max(f[lc],f[rc]);
return;
}
long long gaia(long long x,long long l,long long r,long long ql,long long qr)
{
if(ql<=l&&r<=qr)return f[x];
pushdown(x,l,r);
long long mid=(l+r)>>1,lc=(x<<1),rc=(x<<1)|1,h=0;
if(ql<=mid)h=gaia(lc,l,mid,ql,qr);
if(qr>mid)h=max(h,gaia(rc,mid+1,r,ql,qr));
return h;
}
void dfs(long long x,long long y)
{
dfn[x]=++num;
for(int i=0;i<t[x].size();i++)dfs(t[x][i],x);
out[x]=num;
return;
}
int main()
{
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
l.push(n+1);
a[n+1]=1e6+5;
for(int i=n;i>=1;i--)
{
while(l.size()&&a[l.top()]<=a[i])l.pop();
t[l.top()].push_back(i);
l.push(i);
}
dfs(n+1,0);
for(int i=1;i<m;i++)dijah(1,1,n+1,dfn[i],out[i],1);
for(int i=1;i<=n-m+1;i++)
{
dijah(1,1,n+1,dfn[i+m-1],out[i+m-1],1);
if(i!=1)dijah(1,1,n+1,dfn[i-1],dfn[i-1],-1e9);
printf("%lld ",gaia(1,1,n+1,1,n+1));
}
return 0;
}
Tree Queries
题目描述
给定一棵
解题思路
根节点特殊处理,先来看别的节点。
对于一个节点
若在其子树内,设其属于
第一种情况用
时间复杂度为
Code
#include<bits/stdc++.h>
using namespace std;
const long long mod=998244353;
long long n,m,siz[150005],deep[150005],fa[150005],son[150005],dfn[150005],out[150005],top[150005],num,f[600005],d[600005],t[150005];
vector<long long> a[150005];
long long poww(long long x,long long y)
{
long long h=1;
while(y)
{
if(y&1)
{
h*=x;
h%=mod;
}
x*=x;
x%=mod;
y>>=1;
}
return h;
}
void up(long long x)
{
f[x]=f[x<<1]+f[(x<<1)|1];
return;
}
void galaxy(long long x,long long l,long long r,long long v)
{
f[x]+=(r-l+1)*v;
d[x]+=v;
f[x]%=mod;
d[x]%=mod;
return;
}
void pushdown(long long x,long long l,long long r)
{
if(d[x]==0)return;
long long lc=(x<<1),rc=(x<<1)|1,mid=(l+r)>>1;
galaxy(lc,l,mid,d[x]);
galaxy(rc,mid+1,r,d[x]);
d[x]=0;
return;
}
void dijah(long long x,long long l,long long r,long long ql,long long qr,long long v)
{
if(ql<=l&&r<=qr)
{
galaxy(x,l,r,v);
return;
}
pushdown(x,l,r);
long long lc=(x<<1),rc=(x<<1)|1,mid=(l+r)>>1;
if(ql<=mid)dijah(lc,l,mid,ql,qr,v);
if(qr>mid)dijah(rc,mid+1,r,ql,qr,v);
up(x);
return;
}
long long gaia(long long x,long long l,long long r,long long ql,long long qr)
{
if(ql<=l&&r<=qr)
{
return f[x];
}
pushdown(x,l,r);
long long lc=(x<<1),rc=(x<<1)|1,mid=(l+r)>>1,h=0;
if(ql<=mid)h+=gaia(lc,l,mid,ql,qr);
if(qr>mid)h+=gaia(rc,mid+1,r,ql,qr);
return h%mod;
}
void dfs1(long long x,long long y)
{
deep[x]=deep[y]+1;
fa[x]=y;
siz[x]=1;
for(int i=0;i<a[x].size();i++)
{
if(a[x][i]==y)continue;
dfs1(a[x][i],x);
if(siz[a[x][i]]>siz[son[x]])son[x]=a[x][i];
siz[x]+=siz[a[x][i]];
}
return;
}
void dfs2(long long x,long long y)
{
dfn[x]=++num;
if(son[x]==0)
{
out[x]=num;
return;
}
top[son[x]]=top[x];
dfs2(son[x],x);
for(int i=0;i<a[x].size();i++)
{
if(a[x][i]==y||a[x][i]==son[x])continue;
top[a[x][i]]=a[x][i];
dfs2(a[x][i],x);
}
out[x]=num;
return;
}
long long query(long long x)
{
long long h=0;
while(top[x]!=1)
{
x=top[x];
h+=(n-siz[x])*t[fa[x]];
x=fa[x];
h%=mod;
}
return h;
}
int main()
{
long long x,y,z;
scanf("%lld%lld",&n,&m);
for(int i=1;i<n;i++)
{
scanf("%lld%lld",&x,&y);
a[x].push_back(y);
a[y].push_back(x);
}
dfs1(1,0);
top[1]=1;
dfs2(1,0);
for(int i=1;i<=m;i++)
{
scanf("%lld%lld",&x,&y);
if(x==1)
{
scanf("%lld",&z);
dijah(1,1,n,dfn[y],dfn[y],n*z);
if(dfn[y]!=1)dijah(1,1,n,1,dfn[y]-1,siz[y]*z);
if(out[y]!=n)dijah(1,1,n,out[y]+1,n,siz[y]*z);
t[y]+=z;
if(son[y]!=0)dijah(1,1,n,dfn[son[y]],out[son[y]],(n-siz[son[y]])*z);
}
else
{
printf("%lld\n",(gaia(1,1,n,dfn[y],dfn[y])+query(y))*poww(n,mod-2)%mod);
}
}
return 0;
}
[USACO19DEC] Bessie's Snow Cow P
题目描述
农场下雪啦!Bessie 和往年开冬一样在堆雪牛。她之前是个写实派,总是想把她的雪牛堆得和个真牛一样。但今年不一样,受到来自东方的神秘力量的影响,她想来点抽象艺术,因此她想堆成一棵树的样子。这棵树由
Bessie 要给她的雪牛来点细节。因此她给其中一个雪球加了个鼻子,来表示这是他那抽象的牛的头,并且把它称作雪球
当 Bessie 把一桶颜料泼到一个雪球上时,这个雪球子树上的所有雪球也会被染色(我们称雪球
救救孩子吧!
解题思路
我们先用
若颜色被重新覆盖,那一定是一个子树重新覆盖,所以,我们泼颜色时,可以检索
同时,若其祖先结点被泼过,那么该节点就不用泼,因为一个节点会覆盖其子树内节点,所以,若
查询输出子树和,时间复杂度
Code
#include<bits/stdc++.h>
using namespace std;
long long n,m,dfn[100005],num,f[400005],d[400005],re[100005],out[100005];
vector<long long> a[100005];
set<long long> l[1000005];
void galaxy(long long x,long long l,long long r,long long v)
{
f[x]+=(r-l+1)*v;
d[x]+=v;
return;
}
void pushdown(long long x,long long l,long long r)
{
if(d[x]==0)return;
long long lc=(x<<1),rc=(x<<1)|1,mid=(l+r)>>1;
galaxy(lc,l,mid,d[x]),galaxy(rc,mid+1,r,d[x]);
d[x]=0;
return;
}
void dijah(long long x,long long l,long long r,long long ql,long long qr,long long v)
{
if(ql<=l&&r<=qr)return galaxy(x,l,r,v),void();
pushdown(x,l,r);
long long mid=(l+r)>>1,lc=(x<<1),rc=(x<<1)|1;
if(ql<=mid)dijah(lc,l,mid,ql,qr,v);
if(qr>mid)dijah(rc,mid+1,r,ql,qr,v);
f[x]=f[lc]+f[rc];
return;
}
long long gaia(long long x,long long l,long long r,long long ql,long long qr)
{
if(ql<=l&&r<=qr)return f[x];
pushdown(x,l,r);
long long mid=(l+r)>>1,lc=(x<<1),rc=(x<<1)|1,h=0;
if(ql<=mid)h+=gaia(lc,l,mid,ql,qr);
if(qr>mid)h+=gaia(rc,mid+1,r,ql,qr);
return h;
}
void dfs1(long long x,long long y)
{
dfn[x]=++num;
for(int i=0;i<a[x].size();i++)
{
if(a[x][i]==y)continue;
dfs1(a[x][i],x);
}
out[x]=num,re[dfn[x]]=num;
return;
}
int main()
{
long long x,y;
scanf("%lld%lld",&n,&m);
for(int i=1;i<n;i++)
{
scanf("%lld%lld",&x,&y);
a[x].push_back(y),a[y].push_back(x);
}
dfs1(1,0);
set<long long>::iterator q,w,e;
for(int i=1;i<=m;i++)
{
scanf("%lld",&x);
if(x==1)
{
scanf("%lld%lld",&x,&y);
q=upper_bound(l[y].begin(),l[y].end(),dfn[x]);
if(q!=l[y].begin())
{
q--;
if(*q<=dfn[x]&&re[*q]>=dfn[x])continue;
}
q=lower_bound(l[y].begin(),l[y].end(),dfn[x]);
while(q!=l[y].end()&&*q<=out[x])
{
l[y].erase(q),dijah(1,1,n,*q,re[*q],-1);
q=lower_bound(l[y].begin(),l[y].end(),dfn[x]);
}
dijah(1,1,n,dfn[x],out[x],1);
l[y].insert(dfn[x]);
}
else
{
scanf("%lld",&x);
printf("%lld\n",gaia(1,1,n,dfn[x],out[x]));
}
}
return 0;
}
【CF1857G】 Counting Graphs
题目描述
给定一个有
计算同时满足以下所有条件的带权图的数量:
- 该图无重边与自环。
- 所有的边的权值不大于给定的数
且为正整数。 - 该图有唯一的最小生成树。
- 该图的最小生成树是给定的树。
打印其模
我们说两个图是不同的,当且仅当其边集不同。
解题思路
按
一共有
Code
#include<bits/stdc++.h>
using namespace std;
const long long mod=998244353;
struct datay
{
long long x,y,v;
}a[200005];
long long n,m,f[200005],size1[200005];
long long dijah(long long x,long long y)
{
long long h=1;
while(y)
{
if(y&1)h=(h*x)%mod;
x=(x*x)%mod;
y>>=1;
}
return h;
}
long long search(long long x)
{
if(f[x]!=x)f[x]=search(f[x]);
return f[x];
}
bool cmp(datay q,datay w)
{
return q.v<w.v;
}
void poi()
{
long long x,y,s=1;
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++)f[i]=i,size1[i]=1;
for(int i=1;i<n;i++)scanf("%lld%lld%lld",&a[i].x,&a[i].y,&a[i].v);
sort(a+1,a+n,cmp);
for(int i=1;i<n;i++)
{
x=search(a[i].x),y=search(a[i].y);
s=(s*(dijah(m-a[i].v+1,size1[x]*size1[y]-1)%mod))%mod;
f[y]=x,size1[x]+=size1[y];
}
printf("%lld\n",s);
return;
}
int main()
{
long long qwe;
scanf("%lld",&qwe);
for(int i=1;i<=qwe;i++)poi();
return 0;
}
主席树
【luogu P4755】 Beautiful Pair
题目描述
小 D 有个数列
解题思路
考虑对于每个数,在哪些区间中能取到最大值,找出左边和右边第
对于一个数,是否存在其两边的区间中各选一个数,使得乘积小于等于该数。
我们可以枚举左边或右边,用该数除以枚举的数,查询另一边有多少个数小于等于商,这个可用主席树做。
但这样做时间复杂度是
Code
#include<bits/stdc++.h>
using namespace std;
struct datay
{
int v,lc,rc;
}a[3000005];
int n;
long long t[100005];
int le[100005],ri[100005],m;
long long d[100005],b[100005];
stack<long long> l;
int root[100005],num;
int build(int l,int r)
{
if(l==r)
{
return ++num;
}
int p=++num,mid=(l+r)>>1;
a[p].lc=build(l,mid);
a[p].rc=build(mid+1,r);
return p;
}
int dijah(int x,int l,int r,int k,int v)
{
int h=++num;
a[h]=a[x];
if(l==r)
{
a[h].v++;
return h;
}
int mid=(l+r)>>1;
if(k<=mid)a[h].lc=dijah(a[x].lc,l,mid,k,v);
else a[h].rc=dijah(a[x].rc,mid+1,r,k,v);
a[h].v=a[a[h].lc].v+a[a[h].rc].v;
return h;
}
int gaia(int x,int l,int r,int ql,int qr)
{
if(qr==0)return 0;
if(ql<=l&&r<=qr)
{
return a[x].v;
}
int mid=(l+r)>>1,h=0;
if(ql<=mid)h+=gaia(a[x].lc,l,mid,ql,qr);
if(qr>mid)h+=gaia(a[x].rc,mid+1,r,ql,qr);
return h;
}
int p(long long x)
{
int l1=1,r1=m,mid,h=0;
while(l1<=r1)
{
mid=(l1+r1)>>1;
if(d[mid]<=x)h=max(h,mid),l1=mid+1;
else r1=mid-1;
}
return h;
}
int main()
{
scanf("%lld",&n);
for(int i=1;i<=n;i++)scanf("%lld",&t[i]);
l.push(0);
t[0]=t[n+1]=1e9+5;
for(int i=1;i<=n;i++)
{
while(l.size()!=0&&t[l.top()]<t[i])l.pop();
le[i]=l.top();
l.push(i);
}
while(l.size()!=0)l.pop();
l.push(n+1);
for(int i=n;i>=1;i--)
{
while(l.size()!=0&&t[l.top()]<=t[i])l.pop();
ri[i]=l.top();
l.push(i);
}
for(int i=1;i<=n;i++)le[i]++,ri[i]--;
root[0]=build(1,n);
for(int i=1;i<=n;i++)b[i]=t[i];
sort(b+1,b+n+1);
for(int i=1;i<=n;i++)
{
if(b[i]!=b[i-1])
{
d[++m]=b[i];
}
}
for(int i=1;i<=n;i++)t[i]=p(t[i]);
for(int i=1;i<=n;i++)
{
root[i]=dijah(root[i-1],1,n,t[i],1);
}
int qw=0;
long long s=0;
for(int i=1;i<=n;i++)
{
for(int j=le[i];j<=i;j++)
{
qw=p(d[t[i]]/d[t[j]]);
s+=gaia(root[ri[i]],1,n,1,qw)-gaia(root[i-1],1,n,1,qw);
}
}
cout<<s;
return 0;
}
【CF474F】 Ant colony
题目描述
给出一个长度为
每次给定区间
如果
问有多少个没有得到满分,即
解题思路
首先,得到满分的数一定是整个序列的
我们可以通过
这个问题可以通过
时间复杂度
Code
#include<bits/stdc++.h>
using namespace std;
struct datay
{
int v,lc,rc;
}a[3000005];
int n,b[100005],f[100005][21],p[21],lo[1000005],num,root[1000005],m;
set<int> l;
map<int,int> pp;
int build(int l,int r)
{
if(l==r)
{
return ++num;
}
int mid=(l+r)>>1,h=++num;
a[h].lc=build(l,mid);
a[h].rc=build(mid+1,r);
return h;
}
int dijah(int x,int l,int r,int k,int v)
{
if(l==r)
{
a[++num]=a[x];
a[num].v+=v;
return num;
}
int mid=(l+r)>>1,h=++num;
a[h]=a[x];
if(k<=mid)a[h].lc=dijah(a[x].lc,l,mid,k,v);
else a[h].rc=dijah(a[x].rc,mid+1,r,k,v);
a[h].v=a[a[h].lc].v+a[a[h].rc].v;
return h;
}
int gaia(int x,int l,int r,int ql,int qr)
{
if(ql<=l&&r<=qr)return a[x].v;
int mid=(l+r)>>1,h=0;
if(ql<=mid)h+=gaia(a[x].lc,l,mid,ql,qr);
if(qr>mid)h+=gaia(a[x].rc,mid+1,r,ql,qr);
return h;
}
int query(int x,int y)
{
int z=lo[y-x+1];
return __gcd(f[x][z],f[y-p[z]+1][z]);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&b[i]);
l.insert(b[i]);
f[i][0]=b[i];
}
p[0]=1;
lo[0]=-1;
for(int i=1;i<=20;i++)p[i]=p[i-1]<<1;
for(int i=1;i<=n;i++)lo[i]=lo[i>>1]+1;
for(int i=1;p[i]<=n;i++)
{
for(int j=1;j+p[i]-1<=n;j++)
{
f[j][i]=__gcd(f[j][i-1],f[j+p[i-1]][i-1]);
}
}
int g=0;
set<int>::iterator q=l.begin();
for(;q!=l.end();q++)
{
pp[*q]=++g;
}
for(int i=1;i<=n;i++)b[i]=pp[b[i]];
root[0]=build(1,n);
for(int i=1;i<=n;i++)root[i]=dijah(root[i-1],1,n,b[i],1);
scanf("%d",&m);
int x,y,z;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
z=pp[query(x,y)];
if(z==0)printf("%d\n",y-x+1);
else printf("%d\n",y-x+1-gaia(root[y],1,n,1,z)+gaia(root[x-1],1,n,1,z));
}
return 0;
}
DP
【CF788C】 The Great Mixing
题目描述
有k种可乐,第i瓶可乐的CO2浓度是ai/1000,问要配置出浓度n/1000的可乐,最少需要几瓶可乐。
输入:
第一行n和k。
第二行k个整数,第i个整数表示ai。
输出:一行,表示最少需要几瓶,无解输出-1。
(
解题思路
处理平均数的一个方法:将所有
如何找呢,因为实际最小最大到不了
正解:分析可得,最后构成答案的数按一定顺序排列,存在他们的前缀和的绝对值都小于等于
Code
#include<bits/stdc++.h>
using namespace std;
int n,m,a[1000005];
int f[4000005];
int main()
{
ios::sync_with_stdio(false);
memset(f,1,sizeof(f));
cin>>n>>m;
for(int i=1;i<=m;i++)cin>>a[i],a[i]-=n;
sort(a+1,a+m+1);
int k=unique(a+1,a+m+1)-a-1;
for(int i=1;i<=k;i++)
{
f[2000000+a[i]]=1;
if(a[i]>0)
{
for(int j=-500000;j<=500000;j++)f[j+2000000]=min(f[j+2000000],f[j+2000000-a[i]]+1);
}
else
{
for(int j=500000;j>=-500000;j--)f[j+2000000]=min(f[j+2000000],f[j+2000000-a[i]]+1);
}
}
if(f[2000000]<10000000)cout<<f[2000000];
else cout<<-1;
return 0;
}
【luogu P5851】 [USACO19DEC] Greedy Pie Eaters P
题目描述
Farmer John 有
Farmer John 可以选择一个奶牛序列
解题思路
区间DP,
时间复杂度
Code
#include<bits/stdc++.h>
using namespace std;
struct datay
{
long long l,r,v;
}a[900005];
long long n,m,f[305][305],p[305][305][305];
int main()
{
long long k;
scanf("%lld%lld",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%lld%lld%lld",&a[i].v,&a[i].l,&a[i].r);
for(int j=a[i].l;j<=a[i].r;j++)
{
p[j][a[i].l][a[i].r]=max(p[j][a[i].l][a[i].r],a[i].v);
}
}
for(int i=1;i<=n;i++)
{
for(int j=2;j<=n;j++)
{
for(int u=1;u<=i;u++)
{
k=j+u-1;
if(k<i||k>n)continue;
p[i][u][k]=max(max(p[i][u+1][k],p[i][u][k-1]),p[i][u][k]);
}
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n-i+1;j++)
{
k=i+j-1;
f[j][k]=max(p[j][j][k]+f[j+1][k],p[k][j][k]+f[j][k-1]);
for(int u=j;u<k;u++)f[j][k]=max(f[j][k],f[j][u]+f[u+1][k]);
for(int u=j+1;u<k;u++)f[j][k]=max(f[j][k],f[j][u-1]+f[u+1][k]+p[u][j][k]);
}
}
cout<<f[1][n];
return 0;
}
概率
【CF1753C】Wish I Knew How to Sort
题目描述
给定一个长度为
操作如下:
- 等概率随机选取两个位置
,若 ,则交换 。
注意:当 时,交换失败,也算作一次操作。
请你求出操作被执行的 期望次数。对 998244353 取模。 。
解题思路
设序列中一共有
设原序列前
只有和后面的
设成功一次的期望为
求和即可。
Code
#include<bits/stdc++.h>
using namespace std;
const long long mod=998244353;
long long dijah(long long x,long long y)
{
long long h=1;
while(y)
{
if(y&1)
{
h*=x;
h%=mod;
}
x*=x;
y>>=1;
x%=mod;
}
return h;
}//快速幂
long long n,a[200005];
void poi()
{
long long k=0,m=0,s=0;
scanf("%lld",&n);
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
if(!a[i])k++;
}
for(int j=1;j<=k;j++)
{
if(a[j])m++;
}
for(long long i=1;i<=m;i++)
{
s=(s+dijah(i*i%mod,mod-2))%mod;//求和
}
s*=n;
s%=mod;
s*=(n-1);
s%=mod;
s*=dijah(2,mod-2);
s%=mod;
printf("%lld\n",s);
return;
}
int main()
{
long long qwe;
scanf("%lld",&qwe);
for(int i=1;i<=qwe;i++)poi();
return 0;
}
整体二分
【luogu P3242】[HNOI2015] 接水果
题目描述
风见幽香非常喜欢玩一个叫做 osu! 的游戏,其中她最喜欢玩的模式就是接水果。由于她已经 DT FC 了 The big black,她觉得这个游戏太简单了,于是发明了一个更加难的版本。
首先有一个地图,是一棵由
这颗树上有
接下来依次会有
幽香每次需要选择一个盘子去接当前的水果:一个盘子能接住一个水果,当且仅当盘子的路径是水果的路径的子路径。这里规定:从
当然为了提高难度,对于第
解题思路
分类讨论盘子能接住水果的情况,设盘子从
那么,只要水果的一端在 到 内,另外一端在 到 内就能满足要求 。
设 为 的一个子节点,且 包含 。
水果的一端在 到 或 到 内,另外一端到 到 内就满足要求。
那么我们每个水果可以看成一个点,每个盘子看成一个或两个矩形(第一种或第二种情况),那就是查询包含一个点的所有矩形的第
看到第
在整体二分里面将矩形信息加进数据结构中时,我们可以用扫描线来做,每次存储一列的信息,最后查询即可。
时间复杂度
Code
#include<bits/stdc++.h>
using namespace std;
struct data
{
long long x,y,v,k;
}a[40005];
struct datay
{
long long x,y,k,v,z;
bool ok;
}b[40005],b1[40005],b2[40005];
struct line
{
long long x,l,r,v;
}li[240005];
long long n,m1,m2,dfn[40005],out[40005],num,fa[40005][21],deep[40005],f[40005];
vector<long long> t[40005];
long long search(long long x,long long y)
{
swap(x,y);
for(int i=20;i>=0;i--)
{
if(deep[fa[x][i]]>deep[y])x=fa[x][i];
}
return x;
}
long long lowbit(long long x)
{
return x&(-x);
}
void dijah(long long x,long long y)
{
for(int i=x;i<=n+1;i+=lowbit(i))f[i]+=y;
return;
}
long long gaia(long long x)
{
long long h=0;
while(x)
{
h+=f[x];
x-=lowbit(x);
}
return h;
}
bool LCA(long long x,long long y)
{
if(deep[x]<deep[y])swap(x,y);
for(int i=20;i>=0;i--)
{
if(deep[fa[x][i]]>=deep[y])x=fa[x][i];
}
if(x==y)return true;
return false;
}
bool cmp1(data q,data w)
{
return q.v<w.v;
}
bool cmp2(datay q,datay w)
{
return q.z<w.z;
}
bool cmp3(datay q,datay w)
{
return dfn[q.x]<dfn[w.x];
}
bool cmp4(line q,line w)
{
return q.x<w.x;
}
void dfs1(long long x,long long y)
{
deep[x]=deep[y]+1;
fa[x][0]=y;
dfn[x]=++num;
for(int i=0;i<t[x].size();i++)
{
if(t[x][i]==y)continue;
dfs1(t[x][i],x);
}
out[x]=num;
return;
}
void solve(long long l,long long r,long long L,long long R)
{
long long mid=(L+R)>>1,m3=0,q=0,q1=0,q2=0,qw=0;
for(int i=L;i<=mid;i++)
{
if(!a[i].k)
{
li[++m3].x=dfn[a[i].x];
li[m3].l=dfn[a[i].y];
li[m3].r=out[a[i].y];
li[m3].v=1;
li[++m3].x=out[a[i].x]+1;
li[m3].l=dfn[a[i].y];
li[m3].r=out[a[i].y];
li[m3].v=-1;
}
else
{
li[++m3].x=1;
li[m3].l=dfn[a[i].y];
li[m3].r=out[a[i].y];
li[m3].v=1;
li[++m3].x=dfn[a[i].k];
li[m3].l=dfn[a[i].y];
li[m3].r=out[a[i].y];
li[m3].v=-1;
if(out[a[i].y]==n)continue;
li[++m3].x=dfn[a[i].y];
li[m3].l=out[a[i].k]+1;
li[m3].r=n;
li[m3].v=1;
li[++m3].x=out[a[i].y]+1;
li[m3].l=out[a[i].k]+1;
li[m3].r=n;
li[m3].v=-1;
}
}
sort(li+1,li+m3+1,cmp4);
for(int i=l;i<=r;i++)
{
while(q<m3&&li[q+1].x<=dfn[b[i].x])dijah(li[q+1].l,li[q+1].v),dijah(li[q+1].r+1,-li[q+1].v),q++;
qw=gaia(dfn[b[i].y]);
if(qw==b[i].k)b1[++q1]=b[i],b1[q1].ok=true;
else if(qw>b[i].k)b1[++q1]=b[i];
else b2[++q2]=b[i],b2[q2].k-=qw;
}
for(int i=l;i<=l+q1-1;i++)b[i]=b1[i-l+1];
for(int i=l+q1;i<=r;i++)b[i]=b2[i-l-q1+1];
for(int i=1;i<=q;i++)dijah(li[i].l,-li[i].v),dijah(li[i].r+1,li[i].v);
if(L==R)
{
for(int i=l;i<=r;i++)b[i].v=L;
return;
}
solve(l,l+q1-1,L,mid);
solve(l+q1,r,mid+1,R);
return;
}
int main()
{
long long x,y;
scanf("%lld%lld%lld",&n,&m1,&m2);
for(int i=1;i<n;i++)
{
scanf("%lld%lld",&x,&y);
t[x].push_back(y);
t[y].push_back(x);
}
dfs1(1,0);
for(int i=1;i<=20;i++)
{
for(int j=1;j<=n;j++)fa[j][i]=fa[fa[j][i-1]][i-1];
}
for(int i=1;i<=m1;i++)
{
scanf("%lld%lld%lld",&a[i].x,&a[i].y,&a[i].v);
if(dfn[a[i].x]>dfn[a[i].y])swap(a[i].x,a[i].y);
if(LCA(a[i].x,a[i].y))a[i].k=search(a[i].x,a[i].y);
}
sort(a+1,a+m1+1,cmp1);
for(int i=1;i<=m2;i++)
{
scanf("%lld%lld%lld",&b[i].x,&b[i].y,&b[i].k);
b[i].z=i;
if(dfn[b[i].x]>dfn[b[i].y])swap(b[i].x,b[i].y);
}
sort(b+1,b+m2+1,cmp3);
solve(1,m2,1,m1);
sort(b+1,b+m2+1,cmp2);
for(int i=1;i<=m2;i++)printf("%lld\n",(b[i].ok==0?-1:a[b[i].v].v));
return 0;
}
线段树
【CF558E】A Simple Task
题目描述
题目大意: 给定一个长度不超过10^5的字符串(小写英文字母),和不超过50000个操作。
每个操作 L R K 表示给区间[L,R]的字符串排序,K=1为升序,K=0为降序。
最后输出最终的字符串。
解题思路
线段树的一类应用。
开
每次修改时查询范围内的每个字母出现次数,然后直接26次区间赋值即可。
最后一位一位查询输出即可。
Code
#include<bits/stdc++.h>
using namespace std;
int n,m,f[26][400005],d[26][400005],t[26];
void galaxy(int p,int x,int l,int r,int v)
{
f[p][x]=(r-l+1)*v;
d[p][x]=v;
return;
}
void pushdown(int p,int x,int l,int r)
{
if(d[p][x]==-1)return;
int mid=(l+r)>>1;
galaxy(p,x<<1,l,mid,d[p][x]),galaxy(p,(x<<1)|1,mid+1,r,d[p][x]);
d[p][x]=-1;
return;
}
void dijah(int p,int x,int l,int r,int ql,int qr,int v)
{
if(ql<=l&&r<=qr)
{
galaxy(p,x,l,r,v);
return;
}
pushdown(p,x,l,r);
int mid=(l+r)>>1,lc=(x<<1),rc=(x<<1)|1;
if(ql<=mid)dijah(p,lc,l,mid,ql,qr,v);
if(qr>mid)dijah(p,rc,mid+1,r,ql,qr,v);
f[p][x]=f[p][lc]+f[p][rc];
return;
}
int gaia(int p,int x,int l,int r,int ql,int qr)
{
if(ql<=l&&r<=qr)return f[p][x];
pushdown(p,x,l,r);
int lc=(x<<1),rc=(x<<1)|1,mid=(l+r)>>1,h=0;
if(ql<=mid)h+=gaia(p,lc,l,mid,ql,qr);
if(qr>mid)h+=gaia(p,rc,mid+1,r,ql,qr);
return h;
}
int main()
{
memset(d,-1,sizeof(d));
int l,r,v;
string x;
scanf("%d%d",&n,&m);
cin>>x;
for(int i=0;i<x.size();i++)dijah(x[i]-'a',1,1,n,i+1,i+1,1);
for(int i=1;i<=m;i++)
{
memset(t,0,sizeof(t));
scanf("%d%d%d",&l,&r,&v);
for(int j=0;j<26;j++)t[j]=gaia(j,1,1,n,l,r);
if(v==1)
{
for(int j=0;j<26;j++)dijah(j,1,1,n,l,r,0);
for(int j=0;j<26;j++)
{
if(t[j]==0)continue;
dijah(j,1,1,n,l,l+t[j]-1,1);
l+=t[j];
}
}
else
{
for(int j=0;j<26;j++)dijah(j,1,1,n,l,r,0);
for(int j=25;j>=0;j--)
{
if(t[j]==0)continue;
dijah(j,1,1,n,l,l+t[j]-1,1);
l+=t[j];
}
}
}
for(int i=1;i<=n;i++)
{
for(int j=0;j<26;j++)
{
if(gaia(j,1,1,n,i,i)>=1)
{
cout<<char(j+'a');
}
}
}
return 0;
}
数学
【luogu P8883】幻想中成为原神
题目描述
其中一个问题是这样的:定义一个丘丘人是可以被击杀的,当且仅当存在一个大于
解题思路
正解时整除分块,难打,讲一下另一种解法。
注意到题目中有一句话叫“你的答案与真正的答案有着不超过
逆向思维,不含平方因子的概率为
由欧拉乘积公式
直接把
Code
#include<bits/stdc++.h>
using namespace std;
long long n,x;
int main()
{
scanf("%lld",&n);
for(int i=1;i<=n;i++)
{
scanf("%lld",&x);
long long p=x*(1.0)*(0.39207289814597);
printf("%lld\n",p);
}
return 0;
}
【luogu P10083】 [GDKOI2024 提高组] 不休陀螺
题目描述
有
接下来你可以选择一个区间
开始时你的卡组会按照随机顺序排列并且你有
当你打完这个排列中的卡后你的卡组又会重新随机排列然后你再依次打出,直到你无法再打出下一张牌(当前费用小于下一张牌需要消耗的费用)时停止。
如果一个卡组无论在什么情况下都能够无限打下去,我们则称这卡组可以“陀螺无限”。
现在求有多少个区间组成的卡组能够“陀螺无限”。
解题思路
若一个卡组打完一遍后费用还少了,肯定不行,所以每打完一次你的费用要增加,这个可以用前缀和解决。
考虑极限情况,先加到最少,再加回来,最少点前加的要最多,后面减得也要越多,这样才最有可能减到
最小点的值可以用前缀和解决,设最小点为
上面的数字可以看成
小于
Code
#include<bits/stdc++.h>
using namespace std;
long long n,m,a[1000005],b[1000005],f1[1000005],f[1000005][21],p[21],lo[1000005],f2[1000005],d[1000005],s;
set<long long> ll;
map<long long,long long> pp;
long long gaia(long long x,long long y)
{
long long z=lo[y-x+1];
return max(f[x][z],f[y-p[z]+1][z]);
}
long long lowbit(long long x)
{
return x&(-x);
}
void dijah(long long x,long long y)
{
for(int i=x;i<=n;i+=lowbit(i))d[i]+=y;
return;
}
long long gaia1(long long x)
{
long long h=0;
while(x)
{
h+=d[x];
x-=lowbit(x);
}
return h;
}
int main()
{
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
for(int i=1;i<=n;i++)scanf("%lld",&b[i]),f[i][0]=min(a[i],b[i]);
ll.insert(0);
long long s=0;
for(int i=1;i<=n;i++)f1[i]+=b[i]-a[i]+f1[i-1],ll.insert(f1[i]);
p[0]=1,lo[0]=-1;
for(int i=1;i<=20;i++)p[i]=p[i-1]<<1;
for(int i=1;i<=n;i++)lo[i]=lo[i>>1]+1;
for(int i=1;p[i]<=n;i++)
{
for(int j=1;j+p[i]-1<=n;j++)f[j][i]=max(f[j][i-1],f[j+p[i-1]][i-1]);
}
for(int i=1;i<=n;i++)
{
f2[i]=f2[i-1];
if(b[i]<a[i])f2[i]+=a[i]-b[i];
}
long long g=0;
set<long long>::iterator q=ll.begin();
for(;q!=ll.end();q++)pp[*q]=++g;
f1[0]=pp[0];
for(int i=1;i<=n;i++)f1[i]=pp[f1[i]];
long long r=0;
for(int i=1;i<=n;i++)
{
while(r<n)
{
if(gaia(i,r+1)+f2[r+1]-f2[i-1]<=m)r++,dijah(f1[r],1);
else break;
}
if(r>=i)s+=r-i-gaia1(f1[i-1]-1)+1;
dijah(f1[i],-1);
}
cout<<s;
return 0;
}
网络流
【luogu P10080】 [GDKOI2024 提高组] 匹配
题目描述
给定一个
给定每条边为黑色或白色,你需要找到一个完美匹配,使得匹配里的黑色边数恰好为偶数。
如果你对二分图的定义有疑问:
- 二分图是一个无向图,点分为左右两部分,每部分各
个点,每条边都连接两个属于不同部分的点。 - 一个完美匹配是一个大小为
的边的集合,使得每个点都恰好与集合里的一条边相连。
( )
解题思路
如果没有黑色边数恰好为偶数的限制,那么直接用网络流跑即可。
我们求出一个完美匹配方案,黑色边方案为偶数直接输出,为奇数就进行调整。
因为网络流跑满的边在残量网络中会反过来,利用这条性质,我们可以发现,若网络流残量网络上存在一个环,且环上的黑色边数为奇数,那么将这个环上的边全部反过来就可以了,黑色边数会变成偶数。
找奇环每个点只用
最后输出方案即可。
Code
#include<bits/stdc++.h>
using namespace std;
struct datay
{
int nex,to,maxx;
}a[1000005];
int n,m,start,endd,head[10005],num=-1,d[10005],now[10005],b[1000005],r,v1[1000005],vv[10005][2],v2[10005][2];
queue<int> l;
bool can;
void add(int x,int y,int z)
{
a[++num].to=y,a[num].maxx=z,a[num].nex=head[x];
head[x]=num;
return;
}
void edge(int x,int y,int z)
{
add(x,y,z),add(y,x,0);
return;
}
bool bfs()
{
memset(d,-1,sizeof(d));
queue<int> l;
l.push(start),d[start]=0,now[start]=head[start];
int x,u,v;
while(l.size())
{
x=l.front();
l.pop();
for(int i=head[x];i!=-1;i=a[i].nex)
{
u=a[i].to,v=a[i].maxx;
if(v>0&&d[u]==-1)d[u]=d[x]+1,now[u]=head[u],l.push(u);
}
}
if(d[endd]==-1)return false;
return true;
}
int dfs(int x,int y)
{
if(x==endd)return y;
int now_y=y,u,v,fl;
for(;now[x]!=-1;now[x]=a[now[x]].nex)
{
if(now_y==0)break;
u=a[now[x]].to,v=a[now[x]].maxx;
if(v>0&&d[u]==d[x]+1)
{
fl=dfs(u,min(v,now_y));
now_y-=fl,a[now[x]].maxx-=fl,a[now[x]^1].maxx+=fl;
}
}
return y-now_y;
}
int dinic()
{
int s=0;
while(bfs())s+=dfs(start,1e9+5);
return s;
}
void dijah(int x,int y)
{
if(vv[x][!(y&1)]!=-1)
{
y=x;
for(int i=r;i>=1;i--)
{
a[b[i]].maxx^=1,a[b[i]^1].maxx^=1;
x=a[b[i]^1].to;
if(x==y)break;
}
can=true;
return;
}
if(vv[x][(y&1)]!=-1)return;
vv[x][y&1]=y;
for(int i=head[x];i!=-1;i=a[i].nex)
{
if(a[i].maxx>0)
{
b[++r]=i;
if(!v2[a[i].to][(y+v1[i/2+1])&1])dijah(a[i].to,(y+v1[i/2+1])&1);
if(can)return;
r--;
}
}
vv[x][y&1]=-1,v2[x][y&1]=1;
return;
}
bool check()
{
int h=0;
for(int i=0;i<2*m;i+=2)
{
if(!a[i].maxx)h+=v1[i/2+1];
}
if(h&1)return false;
return true;
}
void put()
{
for(int i=0;i<2*m;i+=2)
{
if(!a[i].maxx)printf("%d ",i/2+1);
}
printf("\n");
return;
}
void poi()
{
memset(vv,-1,sizeof(vv));
memset(v1,0,sizeof(v1));
memset(head,-1,sizeof(head));
int x,y,z;
scanf("%d%d",&n,&m);
can=false,r=0,num=-1,start=2*n+1,endd=2*n+2;
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&v1[i]);
if(x>y)swap(x,y);
edge(x,y,1);
}
for(int i=1;i<=n;i++)edge(start,i,1),edge(i+n,endd,1);
int p=dinic();
if(p<n)
{
cout<<"-1\n";
return;
}
if(check())
{
put();
return;
}
memset(v2,0,sizeof(v2));
for(int i=1;i<=endd;i++)
{
if(!v2[i][0])dijah(i,0);
if(can)break;
}
if(!can)
{
cout<<"-1\n";
return;
}
put();
return;
}
int main()
{
int qwe;
scanf("%d",&qwe);
for(int i=1;i<=qwe;i++)poi();
return 0;
}
贪心
【GJOI 2024.2.16 T1】 楼房搭建
题目描述
一个长度为
解题思路
贪心。
使前面的所用次数最少,但这样做很明显是错的。
考虑反悔贪心。
- 若
,现在变成 次 ,相当于用一次操作给 。 - 若
,变成 ,即 ,相当于用一次操作给 。 - 若
,变成三次 ,相当于两次操作给 。
所以,对于一个
让
Code
#include<bits/stdc++.h>
using namespace std;
long long n,a[1000005],x,y,s,f[1000005];
int main()
{
scanf("%lld",&n);
for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
for(int i=1;i<=n;i++)
{
if(a[i]<0)a[i]=0;
x=min(a[i]/3,f[i]);
a[i]-=3*x;
y=a[i]/2,a[i]-=2*y;
s+=x+y+a[i];
f[i+1]+=x*2+y,a[i+1]-=a[i]*2+y;
}
cout<<s;
return 0;
}
容斥
【GJOI 2024.2.16 T3】 数字收藏
题目描述
有
解题思路
考虑加入的数或删除的数跟原数集的数有多少个的最大公因数为
若
否则将该数除以
设
时间复杂度
Code
#include<bits/stdc++.h>
using namespace std;
long long n,m,s,mu[200005],prime[200005],l,f[200005],b[200005];
bool v[200005];
vector<long long> a[100005];
int main()
{
mu[1]=1;
for(int i=2;i<=100000;i++)
{
if(!v[i])prime[++l]=i,mu[i]=-1;
for(int j=1;j<=l;j++)
{
if(i*prime[j]>100000)break;
v[i*prime[j]]=1;
if(i%prime[j]==0)break;
mu[i*prime[j]]=-mu[i];
}
}
for(int i=1;i<=100000;i++)
{
for(int j=i;j<=100000;j+=i)a[j].push_back(i);
}
long long x,y;
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%lld%lld",&x,&y);
if(y%m!=0)
{
printf("%lld\n",s);
continue;
}
y/=m;
if(x==1)
{
b[y]++;
for(int j=0;j<a[y].size();j++)s+=mu[a[y][j]]*f[a[y][j]];
for(int j=0;j<a[y].size();j++)f[a[y][j]]++;
}
else
{
if(b[y]!=0)
{
b[y]--;
for(int j=0;j<a[y].size();j++)f[a[y][j]]--;
for(int j=0;j<a[y].size();j++)s-=mu[a[y][j]]*f[a[y][j]];
}
}
printf("%lld\n",s);
}
return 0;
}
【GJOI 2024.2.17 T2】 赢家
题目描述
给你一个
解题思路
考虑用总方案
枚举包含
如何求
枚举子集的方法:令
总时间复杂度
Code
#include<bits/stdc++.h>
#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
using namespace std;
const long long mod=1e9+7;
long long n,m,qwe,b[25][25],d[40005],p[1005],f1[40005],f2[40005],s;
int main()
{
long long x,y;
scanf("%lld%lld%lld",&n,&m,&qwe);
for(int i=1;i<=m;i++)
{
scanf("%lld%lld",&x,&y);
b[x][y]=b[y][x]=1;
}
p[0]=1;
for(int i=1;i<=n*n;i++)p[i]=(p[i-1]*2)%mod;
for(int i=0;i<p[n];i++)
{
for(int j=1;j<=n;j++)
{
if(((1<<(j-1))&i))
{
d[i]=d[i^(1<<(j-1))];
for(int u=1;u<=n;u++)
{
if((1<<(u-1))&(i^(1<<(j-1))))d[i]+=b[j][u];
}
break;
}
}
}
for(int i=0;i<p[n];i++)
{
if(i&1)
{
f1[i]=p[d[i]];
for(int j=(i-1)&i;j>0;j=(j-1)&i)f1[i]-=(f1[j]*p[d[j^i]])%mod;
f1[i]%=mod,f1[i]+=mod,f1[i]%=mod;
}
if(i&2)
{
f2[i]=p[d[i]];
for(int j=(i-1)&i;j>0;j=(j-1)&i)f2[i]-=(f2[j]*p[d[j^i]])%mod;
f2[i]%=mod,f2[i]+=mod,f2[i]%=mod;
}
}
s=p[m];
for(int i=0;i<p[n];i++)
{
if(i&1)
{
for(int j=(i+1)|i;j<p[n];j=(j+1)|i)
{
if((i^j)&2)
{
x=p[n]-1-j;
if(d[i^x]+d[i^j^x]-d[x]==m)s-=((f1[i]*f2[i^j])%mod)*p[d[x]]%mod;
}
}
}
}
s%=mod,s+=mod,s%=mod;
cout<<s;
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!