7.31
作者:互联网
CF1709E
题意:
给定一颗树,每个节点上都有数字,修改最少的节点上的数字,让树上不存在一条路径,异或后权值等于\(0\)。
\(1\leq n\leq 2*10^5,1\leq a_i<2^{30}\)
题解:
首先考虑如果一个点,修改了它的点值后,它的子节点都不需要考虑,因为可以把它改成一个很大的数字,永远不会异或成\(0\)
然后就可以考虑启发式合并了,但是有一点难搞的是,合并上来之后,每个点都要异或一个根节点的值才是真正的值。
孙佬教了我一个非常巧妙的做法,他为每个\(set\)设置一个值,\(set\)里面的所有数字异或上这个数字才是那个节点到当前根节点路径上的异或值,这样每次只要改对应的一个数字就可以。
剩下的就交给启发式合并了
#include<bits/stdc++.h>
using namespace std;
namespace red{
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define lowbit(i) ((i)&(-i))
#define mid ((l+r)>>1)
#define eps (1e-8)
const int N=2e5+10,inf=2e9,mod=998244353;
int n,m,sum;
set<int> q[N];
int ret[N],val[N];
vector<int> eg[N];
inline void dfs(int now,int fa)
{
int son=0;
for(int t:eg[now])
{
if(t==fa) continue;
dfs(t,now);
if(q[t].size()>q[son].size()) son=t;
}
//cout<<now<<' '<<son<<"!!"<<endl;
ret[now]=val[now];
ret[now]^=ret[son];
q[now].swap(q[son]);
if(q[now].count(val[now]^ret[now])||q[now].count(ret[now]))
{
++sum;
q[now].clear();
ret[now]=0;
return;
}
q[now].insert(val[now]^ret[now]);
for(int t:eg[now])
{
if(t==fa||t==son) continue;
for(int x:q[t])
{
x^=ret[t];
if(q[now].count(x^ret[now]))
{
++sum;
q[now].clear();
ret[now]=0;
return;
}
}
for(int x:q[t])
{
x^=ret[t]^ret[now]^val[now];
q[now].insert(x);
}
}
}
inline void main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int qwq=1;
while(qwq--)
{
cin>>n;
for(int i=1;i<=n;++i) cin>>val[i];
for(int i=1;i<n;++i)
{
int x,y;cin>>x>>y;
eg[x].emplace_back(y);
eg[y].emplace_back(x);
}
dfs(1,0);
cout<<sum<<'\n';
}
}
}
signed main()
{
red::main();
return 0;
}
/*
*/
CF1709F
题意:
给定\(n,k,f\)
对于长度为从\(1\)到\(n\)的所有二进制串\(s\),需要为\(c_s\)选择一个\([0,k]\)之间的值,满足以\(s\)为前缀的多重集合中二进制串的数量不超过\(c_s\)
在这种情况下,多重集合中的二进制串的最大可能数量是\(f\),求方案数。
\(1\leq n\leq 15,1\leq k,f\leq2*10^5\)
题解:
如果\(f>2*k\)是无解的,因为\(0\)和\(1\)开头的二进制串最多各有\(k\)个
转化题意,把二进制串视作一颗二叉树,\(0\)代表左儿子,\(1\)代表右儿子,对于一个限制\(c_s\),表示代表二进制串\(s\)的节点的子节点权值和不能超过\(c_s\)
那么\(f\)的限制就变成了,该二叉树恰好有节点的权值和恰好为\(f\)的方案数。
以此列一个方程:
\(dp[x][y]\)表示以\(x\)为根节点,子节点权值和恰好为\(y(y\leq k)\)的方案数。
\[dp[x][y]=\sum_{i+j>y}^kdp[son[x][0]][i]*dp[son[x][1]][j]+\sum_{i+j==y}dp[son[x][0]][i]*dp[son[x][1]][j]*(k-y+1) \]解释一下含义,首先是左右儿子权值加起来,如果超过了自己的权值和\(y\),那么子节点无法分配到最大值,但是不影响它的合法的方案。
第二个求和是左右儿子权值加起来恰好为\(y\),那么自己的\(c_x\)有\((k-y+1)\)种选择,为什么前面没有多种选择呢?因为根据”最大可能原则“,如果前面\(c_x\)变大了,那么该节点的权值和就不是\(y\)而是更大了,只有当子节点权值和恰好为\(y\)的时候,自己的\(c_x\)才能选的大一些。
注意,最后一轮的时候\(dp\)方程要化为朴素的\(dp=dp*dp\),因为根节点其实是没有\(c\)的限制的。
方程就是一个很好看的卷积,可以直接卷。
一次卷积,可以让树高增加一,卷积\(n\)次就可以求出所有答案。
#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define lowbit(i) ((i)&(-i))
#define eps (1e-8)
const int N=1e6+10,inf=2e9,mod=998244353,g=3,gi=332748118;
int n,k,f;
int pos[N];
int limit,len;
inline int fast(int x,int k)
{
int ret=1;
while(k)
{
if(k&1) ret=ret*x%mod;
x=x*x%mod;
k>>=1;
}
return ret;
}
inline vector<int> ntt(vector<int> a,int inv)
{
for(int i=0;i<limit;++i)
if(i<pos[i]) swap(a[i],a[pos[i]]);
for(int mid=1;mid<limit;mid<<=1)
{
int Wn=fast(inv?g:gi,(mod-1)/(mid<<1));
for(int r=mid<<1,j=0;j<limit;j+=r)
{
int w=1;
for(int k=0;k<mid;++k,w=w*Wn%mod)
{
int x=a[j+k],y=w*a[j+k+mid]%mod;
a[j+k]=x+y;
if(a[j+k]>=mod) a[j+k]-=mod;
a[j+k+mid]=x-y;
if(a[j+k+mid]<0) a[j+k+mid]+=mod;
}
}
}
if(inv) return a;
inv=fast(limit,mod-2);
for(int i=0;i<limit;++i) a[i]=a[i]*inv%mod;
return a;
}
inline vector<int> mul(vector<int> a,vector<int> b,int n,int m)
{
limit=1,len=0;
while(limit<=n+m) limit<<=1,++len;
for(int i=0;i<limit;++i)
pos[i]=(pos[i>>1]>>1)|((i&1)<<(len-1));
a.resize(limit+1),b.resize(limit+1);
// cout<<n<<' '<<m<<' '<<limit<<"!!"<<endl;
// for(int i=0;i<=limit;++i)
// {
// cout<<a[i]<<' '<<b[i]<<"???"<<endl;
// }
a=ntt(a,1);b=ntt(b,1);
vector<int> c(limit+1,0);
for(int i=0;i<=limit;++i)
{
c[i]=a[i]*b[i]%mod;
}
c=ntt(c,0);
// for(int i=0;i<=limit;++i) cout<<c[i]<<' ';
// cout<<endl;
return c;
}
inline void main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>k>>f;
if(f>2*k)
{
cout<<0<<'\n';
return;
}
vector<int> F(k+1,1);
int ans=0;
for(int i=1;i<=n;++i)
{
vector<int> FF=mul(F,F,k,k);
if(i==n) F=FF;
else
{
vector<int> F2(k+1,0);
int sum=0;
for(int j=2*k;j>=0;--j)
{
if(j<=k)
{
F2[j]=(sum+FF[j]*(k-j+1))%mod;
}
sum+=FF[j];
sum%=mod;
}
swap(F,F2);
}
}
cout<<F[f]<<'\n';
}
}
signed main()
{
red::main();
return 0;
}
/*
*/
CF1187D
题意:
给定数组\(a,b\),每次可以选择\(a\)数组的一段子区间,把它从小到大排序。问能不能通过这种操作把\(a\)数组变成\(b\)数组?
\(n\leq 3*10^5\)
题解:
先判断掉\(a,b\)数组种数字出现次数是不是相同。
根据冒泡排序的原理,只要每次交换长度为\(2\)的区间,最后一样能达到要求(冒泡排序就是总是交换长度为\(2\)的区间最后给数组排序的)
那么如果每次交换长度为\(2\)的区间,除了要移动的数字以外,其他数字的相对顺序是不变的。
所以把\(b\)种的数字一个一个找对应的\(a\)中的数字,比如当前\(b_i\)对应的是\(a_j\),那么\(a_j\)能移动到\(i\)位置当且仅当\(a_j\)是\(a_i\sim a_j\)中最小的元素,这步可以用数据结构实现。
实现后,可以把\(j\)位置改成无穷大(等于删除了\(a_j\))
但是\(a_j\)删除后,某些位置的元素会向右移动一个位置,这个比较难搞。
其实可以无视这部分,因为把\(a_j\)移动过来后也是要删除,我们可以认为\(a_j\)从没有移动过,每次询问时,因为是从左到右处理\(b_i\)对应的数字,所以可以每次直接查询\(a_1\sim a_j\)中的最小值。
#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define double long double
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define lowbit(i) ((i)&(-i))
#define mid ((l+r)>>1)
#define eps (1e-15)
const int N=5e5+10,mod=1e9+7,inv2=5e8+4,inf=2e9;
const double pi=acos(-1.0);
int n;
int a[N],b[N],c[N],d[N];
vector<int> pos[N];
int num[N];
typedef pair<int,int> pr;
struct seg
{
int ans[N<<2];
inline void build(int l,int r,int p)
{
if(l==r)
{
ans[p]=a[l];
return;
}
build(l,mid,ls(p));
build(mid+1,r,rs(p));
ans[p]=min(ans[ls(p)],ans[rs(p)]);
}
inline void update(int pos,int l,int r,int p,int k)
{
if(l==r) {ans[p]=k;return;}
if(pos<=mid) update(pos,l,mid,ls(p),k);
else update(pos,mid+1,r,rs(p),k);
ans[p]=min(ans[ls(p)],ans[rs(p)]);
}
inline int query(int tl,int tr,int l,int r,int p)
{
if(tl<=l&&r<=tr) return ans[p];
int minn=inf;
if(tl<=mid) minn=min(minn,query(tl,tr,l,mid,ls(p)));
if(tr>mid) minn=min(minn,query(tl,tr,mid+1,r,rs(p)));
return minn;
}
}T;
inline void main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int qwq;cin>>qwq;
while(qwq--)
{
cin>>n;
for(int i=1;i<=n;++i)
{
pos[i].clear();
num[i]=0;
}
for(int i=1;i<=n;++i)
{
cin>>a[i];
c[i]=a[i];
pos[a[i]].emplace_back(i);
}
for(int i=1;i<=n;++i)
{
cin>>b[i];
d[i]=b[i];
}
sort(c+1,c+n+1);sort(d+1,d+n+1);
bool flag=0;
for(int i=1;i<=n;++i)
{
if(c[i]!=d[i])
{
flag=1;
}
}
if(flag)
{
cout<<"NO\n";
continue;
}
T.build(1,n,1);
for(int i=1;i<=n;++i)
{
int r=pos[b[i]][num[b[i]]++];
int tmp=T.query(1,r,1,n,1);
// cout<<r<<' '<<tmp<<"!!"<<endl;
if(tmp==b[i])
{
T.update(r,1,n,1,inf);
}
else flag=1;
}
if(flag) cout<<"NO\n";
else cout<<"YES\n";
}
}
}
signed main()
{
red::main();
return 0;
}
/*
1
6
9 3 2 5 6 1
2 3 1 5 6 9
2 3 9 5 6 1
2 3 1 5 6 9
1-3,3-6,1-6
2-6,3-2,4-3,5-4,6-5
*/
CF1187F
题意:
有一个长度为 \(n\) 的数列 \(\{x_i\}\),定义 \(B(x)\) 为将 \(x\) 划分为所有元素都相等的子段的最小子段数。
对于位置\(i\), \(1\leq i\leq n\),它的值\(x_i\) 将等概率为 \([l_i,r_i]\) 中的一个整数。
求 \((B(x))^2\) 的期望,即 \(E((B(x))^2)\),对 \(10^9+7\) 取模。
\(n\leq 2*10^5,1\leq l_i\leq r_i\leq 10^9\)
题解:
首先考虑\(E(B(x))\)
即为\(E(\sum_{i=1}^n[x_i\neq x_{i-1}])\),这个比较好求
设\(R(i)=E([x_i\neq x_{i-1}])\)
里面的概率是\(1-\frac{[l_i,r_i]∩[l_{i-1},r_{i-1}]}{len_i*len_{i-1}}\)
然后求\(E(B(x)^2)=E(\sum_{i=1}^n[x_i\neq x_{i-1}]\sum_{j=1}^n[x_j\neq x_{j-1}])\)
讨论每一对位置的贡献
如果\(i=j\),那么贡献就是\(R(i)\)
如果\(|i-j|>1\),那么两对位置没有关系,贡献就是\(R(i)*R(j)\)
如果\(|i-j|=1\),两对位置相邻,要考虑两对位置同时不同的概率:
\(1-E(x_i=x_{i-1})-E(x_i=x_{i+1})+E(x_{i-1}=x_i=x_{i+1})\)
#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define double long double
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define lowbit(i) ((i)&(-i))
#define mid ((l+r)>>1)
#define eps (1e-15)
const int N=5e5+10,mod=1e9+7,inv2=5e8+4,inf=2e9;
const double pi=acos(-1.0);
int n,sum,ret;
int l[N],r[N],len[N],inv[N];
int ans[N];
inline int fast(int x,int k)
{
int ret=1;
while(k)
{
if(k&1) ret=ret*x%mod;
x=x*x%mod;
k>>=1;
}
return ret;
}
inline void main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n;
for(int i=1;i<=n;++i) cin>>l[i];
for(int i=1;i<=n;++i) cin>>r[i];
inv[0]=1;
for(int i=1;i<=n;++i)
{
len[i]=r[i]-l[i]+1;
inv[i]=fast(len[i],mod-2);
ans[i]=(1-max(0ll,min(r[i],r[i-1])-max(l[i],l[i-1])+1)*inv[i]%mod*inv[i-1]%mod+mod)%mod;
if(i>2)
{
sum=(sum+ans[i-2])%mod;
}
ret=((ret+ans[i])%mod+2*sum*ans[i]%mod)%mod;
}
for(int i=1;i<n;++i)
{
int p=(1-(1-ans[i])-(1-ans[i+1]))%mod;
int tmp=max(0ll,min(min(r[i-1],r[i+1]),r[i])-max(max(l[i-1],l[i+1]),l[i])+1)*inv[i-1]%mod*inv[i]%mod*inv[i+1]%mod;
ret=(ret+2*(p+tmp))%mod;
}
cout<<(ret%mod+mod)%mod<<'\n';
}
}
signed main()
{
red::main();
return 0;
}
/*
*/
CF1187G
题意:
有一个 \(n\) 个点 \(m\) 条边的无向连通图上有\(k\)人,这些人要到 \(1\) 号点。每个时刻一个人可以走一条边,也可以不走。
一个人如果过了 \(t\) 时刻才到 \(1\) 号点,要付出 \(c\cdot t\) 的代价。
如果一条边在一个时刻一个方向上同时被 \(a\) 个人走,要付出 \(d\cdot a^2\) 的代价。
求最小代价。
\(2\leq n\leq 50,n-1\leq m\leq 50,1\leq k,c,d\leq 50\)
题解:
最小费用最大流
花费与时间有关,可以把图分层,相邻两层之间的边权至少为\(c\)
源点向第\(0\)层有人的点建边,第\(n+k\)层的一号点作为汇点。
难点在于处理同时被\(a\)个人走的代价\(d*a^2\)
可以把一条边拆成\(k\)条边,第\(i\)边的代价是\(d*(i^2-(i-1)^2)+c\)(差分建图)
#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define double long double
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define lowbit(i) ((i)&(-i))
#define mid ((l+r)>>1)
#define eps (1e-15)
const int N=2e5+10,mod=1e9+7,inv2=5e8+4,inf=2e9;
const double pi=acos(-1.0);
int n,m,k,cc,d;
int st,ed;
struct edge
{
int nxt,to,val,f;
edge(){}
edge(const int &nxt,const int &to,const int &f,const int &val):nxt(nxt),to(to),f(f),val(val){}
}a[N<<1];
int head[N],cnt=1;
inline void link(int x,int y,int f,int z)
{
a[++cnt]=(edge){head[x],y,f,z};head[x]=cnt;
a[++cnt]=(edge){head[y],x,0,-z};head[y]=cnt;
}
int dis[N],c[N],pre[N],eg[N];
bool vis[N];
queue<int> q;
inline bool spfa()
{
memset(dis,0x3f,sizeof(dis));dis[st]=0;
memset(c,0x3f,sizeof(c));
memset(vis,0,sizeof(vis));
q.push(st);pre[ed]=-1;
while(!q.empty())
{
int now=q.front();q.pop();
vis[now]=0;
for(int i=head[now];i;i=a[i].nxt)
{
int t=a[i].to;
if(a[i].f&&dis[t]>dis[now]+a[i].val)
{
dis[t]=dis[now]+a[i].val;
c[t]=min(c[now],a[i].f);
pre[t]=now;
eg[t]=i;
if(!vis[t])
{
vis[t]=1;
q.push(t);
}
}
}
}
return ~pre[ed];
}
inline void dinic()
{
int maxflow=0,mincost=0;
while(spfa())
{
maxflow+=c[ed];
mincost+=c[ed]*dis[ed];
int now=ed;
while(now!=st)
{
a[eg[now]].f-=c[ed];
a[eg[now]^1].f+=c[ed];
now=pre[now];
}
}
cout<<mincost<<'\n';
}
inline void main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n>>m>>k>>cc>>d;
st=0;
for(int i=1;i<=k;++i)
{
int x;cin>>x;
link(st,x,1,0);
}
for(int i=1;i<=m;++i)
{
int x,y;cin>>x>>y;
for(int j=1;j<=n+k;++j)
{
for(int t=1;t<=k;++t)
{
link(x+(j-1)*n,y+j*n,1,d*(t*2-1)+cc);
link(y+(j-1)*n,x+j*n,1,d*(t*2-1)+cc);
}
}
}
for(int t=1;t<n+k;++t)
{
for(int i=2;i<=n;++i)
{
link(i+(t-1)*n,i+t*n,inf,cc);
}
link(1+(t-1)*n,1+t*n,inf,0);
}
ed=1+(n+k-1)*n;
dinic();
}
}
signed main()
{
red::main();
return 0;
}
/*
*/
CF863F
题意:
现有一个长度为 \(n\) 的未知数组 \(A\) , 每个元素都是 \([1,n]\) 内的整数。
有如下两种共 \(q\) 个限制 :
-
\([l,r]\) 中所有数都大于等于 \(v\)
-
\([l,r]\) 中所有数都小于等于 \(v\)
设 \(cnt(i)\) 为 \(i\) 在 \(A\) 中的出现次数。求出在所有满足条件的数组中,下列式子的最小值 :
\[\sum\limits_{i=1}^ncnt(i)^2 \]( $ 1<=n<=50 $ , $ 0<=q<=100 $ ).
$ 1<=t_{i}<=2 $ , $ 1<=l_{i}<=r_{i}<=n $ , $ 1<=v_{i}<=n $ ,
题解:和上面一样,费用流差分建图的板子题,就不放代码了。
标签:7.31,int,sum,leq,mod,now,define 来源: https://www.cnblogs.com/knife-rose/p/16538802.html