题意:
给定\(n\)个点\(m\)条边,选\(k\)个点染红,其他点染蓝,问有多少种方法,让偶数条边两端颜色不同?对\(998244353\)取模
\(1\leq n,m\leq 2*10^5,0\leq k\leq n\)
题解:
假设有\(a\)染红色点的度数和,\(b\)条边两端都是红色的,\(c\)为两端颜色不同的边数
\[a=2*b+c \]如果\(c\)是偶数,那么\(a\)一定要是偶数,直接枚举用了几个奇数度的点染红,
#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=998244353,inv2=5e8+4,inf=2e9; const double pi=acos(-1.0); int n,m,k; int rd[N]; int s[2]; int fac[N],inv[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 init(int n=5e5) { fac[0]=inv[0]=1; for(int i=1;i<=n;++i) fac[i]=fac[i-1]*i%mod; inv[n]=fast(fac[n],mod-2); for(int i=n-1;i>=1;--i) inv[i]=inv[i+1]*(i+1)%mod; } inline int C(int n,int m) { if(n<m||m<0) return 0; return fac[n]*inv[m]%mod*inv[n-m]%mod; } inline void main() { ios::sync_with_stdio(false); cin.tie(0);cout.tie(0); cin>>n>>m>>k; for(int i=1;i<=m;++i) { int x,y;cin>>x>>y; ++rd[x],++rd[y]; } init(); for(int i=1;i<=n;++i) { ++s[rd[i]&1]; } int ans=0; for(int i=0;i<n;i+=2) { ans+=C(s[1],i)*C(s[0],k-i)%mod; ans%=mod; } cout<<ans<<'\n'; } } signed main() { red::main(); return 0; } /* */
题意:
给定数组\(c,t\),每次操作可以让\(c_i=c_{i+1}+c_{i-1}-c_i\)
问能不能通过操作让\(c\)和\(t\)相等。
数组长度\(\leq 10^5\),\(0\leq c_i,t_i\leq 2*10^9\)
题解:
首先,如果\(c_1\neq t_1\)或者\(c_n\neq t_n\),肯定不可能。
设\(a_i=c_{i}-c_{i-1}\)
那么操作过后
\[a_{i+1}=c_{i+1}-(c_{i+1}+c_{i-1}-c_i)=c_{i}-c_{i-1}=a_i\\ a_{i}=(c_{i+1}+c_{i-1}-c_i)-c_{i-1}=c_{i+1}-c_i=a_{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=2e5+10,mod=1e9+7,inv2=5e8+4,inf=2e9; const double pi=acos(-1.0); int n; int a[N],b[N]; inline void main() { ios::sync_with_stdio(false); cin.tie(0);cout.tie(0); cin>>n; bool flag=1; for(int i=1;i<=n;++i) { cin>>a[i]; } for(int i=1;i<=n;++i) { cin>>b[i]; } if(a[1]!=b[1]||a[n]!=b[n]) flag=0; for(int i=n;i>=1;--i) { a[i]-=a[i-1]; b[i]-=b[i-1]; } sort(a+1,a+n+1); sort(b+1,b+n+1); for(int i=1;i<=n;++i) flag&=(a[i]==b[i]); if(flag) cout<<"Yes\n"; else cout<<"No\n"; //c[i]=c[i+1]+c[i-1]-c[i]; //c[i]-c[i-1]=c[i+1]-c[i]; //d[i]=d[i+1] } } signed main() { red::main(); return 0; } /* 7 2 4 12 7 -5 2 8 7 -5 8 2 */
题意:
给定一个序列,求所有子区间和的异或值。
\(n\leq 10^5,\sum a_i\leq 10^6\)
题解:
\(solution1\)
题目要求
\(\bigoplus \sum_{l=0}^n\sum_{r=l+1}^n(s[r]-s[l])\)
二进制数考虑按位
枚举位数\(k\)和右端点,如何快速求有多少满足要求的左端点?
如果右端点是\(1\),那么左端点:
要么是\(0\),且左端点低于第\(k\)位的部分小于等于右端点低于第\(k\)位的部分
要么是\(1\),且左端点低于第\(k\)位的部分大于右端点低于第\(k\)位的部分。
因为后者涉及做减法的时候会不会借位,如果借位,那么第\(k\)位的\(01\)就反转了。
右端点是\(1\)类似。
开两个树状数组,分别维护第\(k\)位是\(0\)和\(1\)的情况。
\(solution2\)
题解区的神仙还能继续优化。
下面引用自 Ntokisq 神仙
如果左右端点的第\(k\)位相同,那么其实就是在求逆序对数。
第\(k\)位不同时,其实是在算顺序对,但是贡献只有不同时才有,不妨用容斥,先不考虑相同不相同,再减去第\(k\)位相同的贡献。
第\(k\)位相同的贡献其实在算逆序对数的时候也算出来了。
那么怎么求序列的逆序对数呢?这个问题肯定是\(O(nlogn)\)的。
但是本题只要求逆序对的奇偶性。
有一个结论:如果一个排列经过\(x\)次交换邻项后变得有序,那么\(x\)和排列的逆序对数奇偶性相同。
可以推导出另一个结论:一个排列的逆序对数的奇偶性与\(n\)减去该排序的环数相同。
最终状态\(p_i=i\),那么有序的排序是有\(n\)个环的。
每次交换,要么减少一个环,要么增加一个环,减少一个环的话,还需要再花操作一次操作加回来,所以奇偶性也是相同。
求排列的环数可以\(O(n)\)
\(solution3\)
下面是另一种解法
可以直接算出每个数字出现多少次,因为算贡献是\(x-y=v\)的形式,且值域不是很大
设\(f_x\)是\(x\)作为区间和的出现次数,\(s_i\)是\(i\)作为前缀和的出现次数
\[f_x=\sum_{i=0}^ns_i*s_{i+x} \]设\(s'\)是\(s\)翻转后的数组
\[f_x=\sum_{i=0}^ns_i*s'_{m-i-x} \]就可以卷积了
\(solution4\)
固定左端点,枚举右端点,就可以求出左端点固定时的\([l,r]\)和的异或值。
那么每次左端点左移一位,其实就是把之前有的所有异或值全部\(+a[l]\),再把\(a[l]\)插入进去
考虑到\(\sum a_i\)很小,可以每次一位一位的加,然后用\(01trie\)实现。
\(01tire\)实现每次给所有数字\(+1\)是可以实现的:考虑倒着建\(trie\)树,第\(k\)层的儿子代表数字\(x\)的第\(k\)位是\(0\)还是\(1\),那么给所有数字\(+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=1e6+10,mod=1e9+7,inv2=5e8+4,inf=2e9; const double pi=acos(-1.0); int n; int a[N],s[N]; struct BIT { int tr[N]; inline void clear() { for(int i=1;i<=1000001;++i) tr[i]=0; } inline void update(int x,int k=1) { for(int i=x;i<=1000001;i+=lowbit(i)) tr[i]+=k; } inline int query(int y) { int sum=0; for(int i=y;i;i-=lowbit(i)) sum+=tr[i]; return sum; } }T[2]; inline void main() { ios::sync_with_stdio(false); cin.tie(0);cout.tie(0); cin>>n; for(int i=1;i<=n;++i) { cin>>a[i]; s[i]=s[i-1]+a[i]; } int ans=0; for(int k=0;k<=20;++k) { T[0].clear();T[1].clear(); int sum=0,tmp=(1<<k)-1; T[0].update(1); for(int i=1;i<=n;++i) { int now=0,val=s[i]&tmp; if((s[i]>>k)&1) { now=T[0].query(val+1)+T[1].query(1000001)-T[1].query(val+1); T[1].update(val+1); } else { now=T[0].query(1000001)-T[0].query(val+1)+T[1].query(val+1); T[0].update(val+1); } now%=2; sum^=now; } if(sum) ans|=(1<<k); } cout<<ans<<'\n'; } } signed main() { red::main(); return 0; } /* 7 2 4 12 7 -5 2 8 7 -5 8 2 */