C.Ramesses and Corner Inversion
将 A [ i ] [ j ] x o r B [ i ] [ j ] A[i][j]\ xor \ B[i][j] A[i][j] xor B[i][j],转成了判断 A A A是否能构成0矩阵。
发现无论如何操作,每行每列1的个数的奇偶性不变。所以若每行每列均有偶数个1,则必然有解(感性证明)。
#include<bits/stdc++.h>
#define gc getchar
#define pb push_back
#define mkp make_pair
#define pii pair<int,int>
#define fi first
#define sc second
using namespace std;
typedef double db;
typedef long long ll;
const int N=505;
int n,m;
int a[N][N],b[N][N];
inline void fl()
{
puts("No");exit(0);
}
int main(){
int i,j,x,y;
scanf("%d%d",&n,&m);
for(i=1;i<=n;++i)
for(j=1;j<=m;++j)
scanf("%d",&a[i][j]);
for(i=1;i<=n;++i)
for(j=1;j<=m;++j)
scanf("%d",&b[i][j]);
for(i=1;i<=n;++i){
for(x=0,j=1;j<=m;++j)
if(a[i][j]^b[i][j]) x^=1;
if(x) fl();
}
for(i=1;i<=m;++i){
for(x=0,j=1;j<=n;++j)
if(a[j][i]^b[j][i]) x^=1;
if(x) fl();
}
puts("Yes");
return 0;
}
D.Frets On Fire
先把 s i s_i si去重,设去重后共有 t t t个数。
设
l
e
n
=
r
−
l
+
1
len=r-l+1
len=r−l+1,
l
e
n
=
1
len=1
len=1时,不同的
s
i
s_i
si所代表区间一定不交,所以贡献为
t
t
t。
随时间增加,不同段所代表区间会相交,如下图,
s
1
+
l
e
n
>
=
2
s_1+len>=2
s1+len>=2,此时
1
1
1的贡献为常数
s
2
−
s
1
s_2-s_1
s2−s1,总贡献为
(
t
−
1
)
(
l
e
n
)
+
s
2
−
s
1
(t-1)(len)+s_2-s_1
(t−1)(len)+s2−s1。
依次类推,贡献可以表达成 f ( x ) = k x + b f(x)=kx+b f(x)=kx+b的一次函数,且关于 x x x分成了 t t t段。
将 s i s_i si升序排序,处理出 d i = s i + 1 − s i d_i=s_{i+1}-s_i di=si+1−si,再将 d i d_i di升序排序,得到两两合并的时间,可以预处理出这个分段函数。询问是二分值域即可。
#include<bits/stdc++.h>
#define gc getchar
#define pb push_back
#define mkp make_pair
#define pii pair<int,int>
#define fi first
#define sc second
using namespace std;
typedef double db;
typedef long long ll;
const int N=2e5+10;
int n,m,cnt,res;
ll a[N],cs;
struct P{
ll k,b,t;
}t[N];
struct pr{
ll d;int id;
bool operator<(const pr&ky)const{
return d<ky.d;
}
}q[N];
inline int fd(ll x)
{
int re,l=1,r=cnt,mid;
for(;l<=r;){
mid=(l+r)>>1;
if(t[mid].t<=x) l=(re=mid)+1;
else r=mid-1;
}
return re;
}
int main(){
int i,j;ll l,r,len;
scanf("%d",&n);
for(i=1;i<=n;++i) scanf("%I64d",&a[i]);
sort(a+1,a+n+1);n=unique(a+1,a+n+1)-a-1;
if(n==1){
for(scanf("%d",&m);m;--m){
scanf("%I64d%I64d",&l,&r);
printf("%I64d ",r-l+1);
}
return 0;
}
for(i=1;i<n;++i) q[i].d=a[i+1]-a[i],q[i].id=i;
sort(q+1,q+n);
t[(cnt=1)].k=n;t[1].b=0;t[1].t=0;
res=n;
for(i=j=1;i<n;i=j){
for(;j<n && q[j].d==q[i].d;++j){
res--;cs+=q[i].d;
}
t[++cnt].k=res;t[cnt].b=cs;t[cnt].t=q[i].d;
}
for(scanf("%d",&m);m;--m){
scanf("%I64d%I64d",&l,&r);
len=r-l+1;
j=fd(len);
printf("%I64d\n",(t[j].k*len+t[j].b));
}
return 0;
}
*E.Pavel and Triangles
每个三元组形如: ( 2 j , 2 i , 2 i ) ( 0 ≤ j ≤ i ) (2^j,2^i,2^i)(0\leq j\leq i) (2j,2i,2i)(0≤j≤i)
考虑 a j a_j aj中的 2 2 2个数能匹配上的 1 1 1个数的选择范围是 a i ( i > j ) a_i(i>j) ai(i>j)的子集 [ a 1 , a j ] ⊂ [ a 1 , a i ] [a_1,a_j]\subset[a_1,a_i] [a1,aj]⊂[a1,ai]。所以贪心让更前面的数匹配更多。
逐个考虑 a 1 − a n a_1-a_n a1−an,每次用尽量多的2取匹配剩下的1,假设还剩下 b i b_i bi个数,那么再匹配 ⌊ b i 3 ⌋ \lfloor\frac{b_i}{3}\rfloor ⌊3bi⌋,最后把 b i % 3 b_i\%3 bi%3加入剩下的1中即可。
#include<bits/stdc++.h>
#define gc getchar
#define pb push_back
#define mkp make_pair
#define pii pair<int,int>
#define fi first
#define sc second
using namespace std;
typedef double db;
typedef long long ll;
const int N=3e5+10;
int n,a[N];
ll ans;int nw,res;
int main(){
int i,j;ll l,r,len;
scanf("%d",&n);
for(i=1;i<=n;++i) {
scanf("%d",&a[i]);
j=min(a[i]>>1,nw);
a[i]-=(j<<1);nw-=j;ans+=j;
ans+=(a[i]/3);a[i]%=3;
nw+=a[i];
}
printf("%I64d",ans);
return 0;
}
*F.Niyaz and Small Degrees
当
x
x
x是定值时,可以
O
(
n
log
n
)
O(n\log n)
O(nlogn)DP求出答案:
设
f
i
,
j
=
(
1
/
0
)
f_{i,j=(1/0)}
fi,j=(1/0)表示
i
i
i子树中所有点度数
≤
x
\leq x
≤x,且不选/选
i
i
i的父边(选则
i
i
i的度数
<
x
<x
<x)时的最优答案。
转移时设
t
o
t
=
∑
k
∈
s
o
n
i
f
k
,
0
tot=\sum \limits_{k\in son_i}f_{k,0}
tot=k∈soni∑fk,0,将点按
f
k
,
1
−
f
k
,
0
f_{k,1}-f_{k,0}
fk,1−fk,0排序,
f
[
i
]
[
j
]
f[i][j]
f[i][j]=前
d
e
g
r
e
e
i
−
x
−
j
degree_i-x-j
degreei−x−j小的
f
k
,
1
−
f
k
,
0
f_{k,1}-f_{k,0}
fk,1−fk,0+
t
o
t
tot
tot
发现此时 d e g r e e i ≤ x degree_i\leq x degreei≤x的点 i i i都是无用的, ( i , j ) ( d e g r e e j ≤ x ) (i,j)(degree_j\leq x) (i,j)(degreej≤x)的边一定会选,而 ( i , j ) ( d e g r e e i > x ) (i,j)(degree_i>x) (i,j)(degreei>x) 的边的选择仅仅由 j j j决定,所以只需要对所有 d e g r e e > x degree>x degree>x的点连通块进行DP。
而
∑
i
=
0
n
−
1
∑
j
=
1
n
[
d
e
g
r
e
e
j
>
i
]
=
∑
i
=
1
n
d
e
g
r
e
e
i
=
2
(
n
−
1
)
\sum\limits_{i=0}^{n-1}\sum\limits_{j=1}^n[degree_j>i]=\sum\limits_{i=1}^ndegree_i=2(n-1)
i=0∑n−1j=1∑n[degreej>i]=i=1∑ndegreei=2(n−1)
总复杂度依旧是
O
(
n
log
n
)
O(n\log n)
O(nlogn)的
对于每个点 i ( d e g r e e i > x ) i(degree_i>x) i(degreei>x),用堆维护所有它指向 d e g r e e ≤ x degree\leq x degree≤x的点的边的边权 w w w,和所有它指向的 d e g r e e > x degree>x degree>x的点 j j j的 f j , 1 − f j , 0 f_{j,1}-f_{j,0} fj,1−fj,0, t o t = ∑ j ∈ s o n i , d e g r e e j > x f j , 0 tot=\sum \limits_{j\in son_i,degree_j>x}f_{j,0} tot=j∈soni,degreej>x∑fj,0,取堆中前 d e g r e e i − x degree_i-x degreei−x小的加入答案即可。
p.s
注意每次要动态插入 f j , 1 − f j , 0 f_{j,1}-f_{j,0} fj,1−fj,0,所以堆不够用了,可以在treap上二分。
若堆中第 k > d e g r e e i − i − j k>degree_i-i-j k>degreei−i−j小的值 < 0 <0 <0,则贪心把堆中值往 t o t tot tot加,直到堆空或者值 ≥ 0 \geq 0 ≥0。
#include<bits/stdc++.h>
#define gc getchar
#define pb push_back
#define mkp make_pair
#define pii pair<int,int>
#define fi first
#define sc second
using namespace std;
typedef double db;
typedef long long ll;
const int N=3e5+10;
const ll inf=1e18;
int n,d[N],id[N],lim;
ll ans[N],f[N][2],nw;
set<pii>e[N];
vector<int>hv[N];
int vs[N],tim;
namespace treap{
#define lc(x) t[x].ch[0]
#define rc(x) t[x].ch[1]
#define F(x) t[x].fa
struct node{
int sz,ch[2],fa,rnd;ll ss,v;
inline void ini(ll x){v=ss=x;rnd=rand();sz=1;}
}t[N<<2];
int cnt,rt[N];
inline void pu(int x)
{
if(!x) return;
t[x].ss=t[lc(x)].ss+t[x].v+t[rc(x)].ss;
t[x].sz=t[lc(x)].sz+1+t[rc(x)].sz;
}
inline void rot(int x)
{
int y=F(x),z=F(y),dr=(rc(y)==x);
t[y].ch[dr]=t[x].ch[dr^1];
if(t[y].ch[dr]) F(t[y].ch[dr])=y;
F(x)=z;if(z) t[z].ch[(rc(z)==y)]=x;
t[x].ch[dr^1]=y;F(y)=x;pu(y);pu(x);
}
int ins(int x,ll v)
{
if(!x) {t[++cnt].ini(v);return cnt;}
int dr=v>t[x].v,y;t[x].sz++;t[x].ss+=v;
t[x].ch[dr]=y=ins(t[x].ch[dr],v);F(y)=x;
if(t[y].rnd>t[x].rnd) rot(y),x=y;
return x;
}
int merge(int x,int y)
{
F(x)=F(y)=0;//!!!
if((!x) || (!y)) return (x|y);
if(t[x].rnd>t[y].rnd){
rc(x)=merge(rc(x),y);F(rc(x))=x;
pu(x);return x;
}else{
lc(y)=merge(x,lc(y));F(lc(y))=y;
pu(y);return y;
}
}
int del(int x,ll v)
{
if(t[x].v==v) return merge(lc(x),rc(x));
int dr=v>t[x].v,y;t[x].sz--;t[x].ss-=v;
t[x].ch[dr]=y=del(t[x].ch[dr],v);F(y)=x;
return x;
}
inline int al(int x)
{
int re=0;
for(;x;){
if(t[x].v<0) {re+=t[lc(x)].sz+1;x=rc(x);}
else x=lc(x);
}
return re;
}
inline ll ask(int x,int kth)
{
ll re=0,vl;int res;
for(;kth;){
res=t[lc(x)].sz+1;vl=t[x].ss-t[rc(x)].ss;
if(kth<res) x=lc(x);
else kth-=res,x=rc(x),re+=vl;
}
return re;
}
}
using namespace treap;
//f[x][0]不一定 <= f[x][1]
void dfs(int x,int fr)
{
vs[x]=tim;ll sum=0;
for(pii y:e[x]) if(y.fi!=fr){
dfs(y.fi,x);sum+=f[y.fi][0];
rt[x]=ins(rt[x],f[y.fi][1]-f[y.fi][0]+y.sc);
}
int pos=al(rt[x]);
f[x][0]=ask(rt[x],max(pos,d[x]-lim))+sum;f[x][1]=ask(rt[x],max(pos,d[x]-lim-1))+sum;
for(pii y:e[x]) if(y.fi!=fr)
rt[x]=del(rt[x],f[y.fi][1]-f[y.fi][0]+y.sc);
}
inline bool cmp(int a,int b){return d[a]>d[b];}
int main(){
int i,j,x,y,z;ll sum=0;
scanf("%d",&n);
for(i=1;i<n;++i){
scanf("%d%d%d",&x,&y,&z);sum+=z;d[x]++;d[y]++;
e[x].insert(mkp(y,z));e[y].insert(mkp(x,z));
}ans[0]=sum;
for(i=1;i<=n;++i) id[i]=i,hv[d[i]].pb(i);
sort(id+1,id+n+1,cmp);
for(lim=1;lim<n;++lim){
tim++;nw=0;
for(int x:hv[lim])
for(pii y:e[x]) if(d[y.fi]>lim)
rt[y.fi]=ins(rt[y.fi],y.sc),e[y.fi].erase(e[y.fi].lower_bound(mkp(x,0)));
for(i=1;i<=n && d[id[i]]>lim;++i) if(vs[id[i]]!=tim){
dfs(id[i],0);nw+=f[id[i]][0];
}
ans[lim]=nw;
}
for(i=0;i<n;++i) printf("%I64d ",ans[i]);
return 0;
}
*G.Get Ready for the Battle
巧妙构造
设 k i = ( ∑ j = 1 i h p j ) % n ( 1 ≤ i < m ) k_i=(\sum \limits_{j=1}^ihp_j)\%n(1\leq i<m) ki=(j=1∑ihpj)%n(1≤i<m), k 0 = 0 , k m = n k_0=0,k_m=n k0=0,km=n,并将 k i k_i ki升序排序。
构造 m m m个组人数分别为 s z i = k i + 1 − k i ( 0 ≤ i < m ) sz_i=k_{i+1}-k_i(0\leq i<m) szi=ki+1−ki(0≤i<m),暴力模拟即可。
这样一定可以达到下界 ⌈ ∑ i = 1 m h p i n ⌉ \lceil\frac{\sum\limits_{i=1}^mhp_i}{n}\rceil ⌈ni=1∑mhpi⌉
#include<bits/stdc++.h>
#define gc getchar
#define pb push_back
#define mkp make_pair
#define pii pair<int,int>
#define fi first
#define sc second
using namespace std;
typedef double db;
typedef long long ll;
const int N=1e6+10;
int n,m,t[N],ss,a[N],cnt;
int v[N],sz[N],ans[N<<2],tot;
int main(){
int i,j,x,y,z;
scanf("%d%d",&n,&m);
for(i=1;i<=m;++i) {
scanf("%d",&t[i]);
ss+=t[i];v[i]=ss%n;
}v[m]=n;
sort(v,v+m+1);
for(i=1;i<=m;++i) sz[i]=v[i]-v[i-1];
for(i=1,j=1;i<=m;++i){
x=t[i];
for(;x>0;){
ans[++tot]=i;
x-=sz[j];
j=(j==m)?1:(j+1);
}
}
for(;tot%m;) ans[++tot]=1;
printf("%d\n",tot/m);
for(i=1;i<=m;++i) printf("%d ",sz[i]);
puts("");
for(i=1;i<=tot;++i){
printf("%d ",ans[i]);
if(!(i%m)) puts("");
}
return 0;
}
H.Triple
暴力FWT:
对于第 i i i个数列,设为多项式 f i f_i fi,其中 f i , a i = x , f i , b i = y , f i , c i = z f_{i,a_i}=x,f_{i,b_i}=y,f_{i,c_i}=z fi,ai=x,fi,bi=y,fi,ci=z,其余项为0, f i f_i fi分别FWT后将 ∏ f w t ( f i ) \prod fwt(f_i) ∏fwt(fi)逆变换就得到了答案。复杂度 O ( n 2 k k ) O(n2^kk) O(n2kk)
优化暴力:
将序列整体异或上 a i a_i ai,即 f i , 0 = x , f i , a i x o r b i = y , f i , a i x o r c i = z f_{i,0}=x,f_{i,a_i\ xor \ b_i}=y,f_{i,a_i\ xor \ c_i}=z fi,0=x,fi,ai xor bi=y,fi,ai xor ci=z。
发现FWT后每一项的系数只可能是以下4种之一:
x
+
y
+
z
,
x
+
y
−
z
,
x
−
y
+
z
,
x
−
y
−
z
x+y+z,x+y-z,x-y+z,x-y-z
x+y+z,x+y−z,x−y+z,x−y−z
单独处理每一位 i ( 0 ≤ i < 2 k ) i(0\leq i<2^k) i(0≤i<2k),假设当前位出现了 a a a个 x + y + z x+y+z x+y+z, b b b个 x + y − z x+y-z x+y−z, c c c个 x − y + z x-y+z x−y+z, d d d个 x − y − z x-y-z x−y−z,则乘积为 ( x + y + z ) a ( x + y − z ) b ( x − y + z ) c ( x − y − z ) d (x+y+z)^a(x+y-z)^b(x-y+z)^c(x-y-z)^d (x+y+z)a(x+y−z)b(x−y+z)c(x−y−z)d
考虑将 f a i x o r b i + = 1 ( 1 ≤ i ≤ n ) f_{a_i\ xor\ b_i}+=1(1\leq i\leq n) fai xor bi+=1(1≤i≤n)和 f a i x o r c i + = 1 f_{a_i\ xor \ c_i}+=1 fai xor ci+=1分别做FWT, c n t b cnt_b cntb表示前者当前位的系数, c n t c cnt_c cntc表示后者当前位的系数。
则
a
+
b
+
c
+
d
=
n
a+b+c+d=n
a+b+c+d=n
a
+
b
−
c
−
d
=
c
n
t
b
a+b-c-d=cnt_b
a+b−c−d=cntb
a
−
b
+
c
−
d
=
c
n
t
c
a-b+c-d=cnt_c
a−b+c−d=cntc
再需要一个等式就可以求出 x , y , z , w x,y,z,w x,y,z,w,所以加一个 f b i x o r c i + = 1 f_{b_i\ xor \ c_i}+=1 fbi xor ci+=1,即 a − b − c + d = c n t a-b-c+d=cnt a−b−c+d=cnt(考虑分别将 f b i , f c i f_{b_i},f_{c_i} fbi,fciFWT后合并, ( 1 ) 2 = ( − 1 ) 2 = 1 , 1 ( − 1 ) = − 1 (1)^2=(-1)^2=1,1(-1)=-1 (1)2=(−1)2=1,1(−1)=−1)
复杂度 O ( 2 k k ) O(2^kk) O(2kk)
#include<bits/stdc++.h>
#define gc getchar
#define pb push_back
#define mkp make_pair
#define pii pair<int,int>
#define fi first
#define sc second
const int mod=998244353;
using namespace std;
typedef double db;
typedef long long ll;
const int N=(1<<17)+100;
int n,m,a,b,c,S,nv2,nv4;
int f[N],g[N],h[N];
inline void ad(int &x,int y){x+=y;if(x>=mod) x-=mod;}
inline void dc(int &x,int y){x-=y;if(x<0) x+=mod;}
inline int inc(int x,int y){x+=y;return x>=mod?x-mod:x;}
inline int dec(int x,int y){x-=y;return x<0?x+mod:x;}
inline int fp(int x,int y)
{
int re=1;
for(;y;y>>=1,x=(ll)x*x%mod)
if(y&1) re=(ll)re*x%mod;
return re;
}
inline void fwt(int *e)
{
int i,j,k,x,y;
for(i=1;i<S;i<<=1){
for(j=0;j<S;j+=(i<<1))
for(k=0;k<i;++k){
x=e[j+k];y=e[i+j+k];
e[j+k]=inc(x,y);e[i+j+k]=dec(x,y);
}
}
}
inline void ifwt(int *e)
{
int i,j,k,x,y;
for(i=1;i<S;i<<=1){
for(j=0;j<S;j+=(i<<1))
for(k=0;k<i;++k){
x=e[j+k];y=e[i+j+k];
e[j+k]=(ll)inc(x,y)*nv2%mod;
e[i+j+k]=(ll)dec(x,y)*nv2%mod;
}
}
}
int main(){
int i,j,x,y,z,w,A,B,C,D,tot=0;
nv2=fp(2,mod-2);nv4=(ll)nv2*nv2%mod;
scanf("%d%d%d%d%d",&n,&m,&a,&b,&c);
S=1<<m;
for(i=1;i<=n;++i){
scanf("%d%d%d",&x,&y,&z);
tot^=x;f[y^x]++;g[z^x]++;h[y^z]++;
}
A=inc(a,inc(b,c));B=dec(inc(a,b),c);
C=inc(dec(a,b),c);D=dec(a,inc(b,c));
fwt(f);fwt(g);fwt(h);
for(i=0;i<S;++i){
x=(ll)inc(n,inc(f[i],inc(g[i],h[i])))*nv4%mod;
y=dec((ll)inc(n,f[i])*nv2%mod,x);
z=dec((ll)inc(n,g[i])*nv2%mod,x);
w=dec((ll)inc(n,h[i])*nv2%mod,x);
f[i]=fp(A,x)*(ll)fp(B,y)%mod*(ll)fp(C,z)%mod*(ll)fp(D,w)%mod;
}
ifwt(f);
for(i=0;i<S;++i) printf("%d ",f[i^tot]);
return 0;
}