【Codeforces】1119 Global Round 2 C-H简要题解

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=rl+1 l e n = 1 len=1 len=1时,不同的 s i s_i si所代表区间一定不交,所以贡献为 t t t
随时间增加,不同段所代表区间会相交,如下图, s 1 + l e n &gt; = 2 s_1+len&gt;=2 s1+len>=2,此时 1 1 1的贡献为常数 s 2 − s 1 s_2-s_1 s2s1,总贡献为 ( t − 1 ) ( l e n ) + s 2 − s 1 (t-1)(len)+s_2-s_1 (t1)(len)+s2s1

依次类推,贡献可以表达成 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+1si,再将 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)(0ji)

考虑 a j a_j aj中的 2 2 2个数能匹配上的 1 1 1个数的选择范围是 a i ( i &gt; j ) a_i(i&gt;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 a1an,每次用尽量多的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的度数 &lt; x &lt;x <x)时的最优答案。
转移时设 t o t = ∑ k ∈ s o n i f k , 0 tot=\sum \limits_{k\in son_i}f_{k,0} tot=ksonifk,0,将点按 f k , 1 − f k , 0 f_{k,1}-f_{k,0} fk,1fk,0排序, f [ i ] [ j ] f[i][j] f[i][j]=前 d e g r e e i − x − j degree_i-x-j degreeixj小的 f k , 1 − f k , 0 f_{k,1}-f_{k,0} fk,1fk,0+ t o t tot tot

发现此时 d e g r e e i ≤ x degree_i\leq x degreeix的点 i i i都是无用的, ( i , j ) ( d e g r e e j ≤ x ) (i,j)(degree_j\leq x) (i,j)(degreejx)的边一定会选,而 ( i , j ) ( d e g r e e i &gt; x ) (i,j)(degree_i&gt;x) (i,j)(degreei>x) 的边的选择仅仅由 j j j决定,所以只需要对所有 d e g r e e &gt; x degree&gt;x degree>x的点连通块进行DP。

∑ i = 0 n − 1 ∑ j = 1 n [ d e g r e e j &gt; 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&gt;i]=\sum\limits_{i=1}^ndegree_i=2(n-1) i=0n1j=1n[degreej>i]=i=1ndegreei=2(n1)
总复杂度依旧是 O ( n log ⁡ n ) O(n\log n) O(nlogn)

对于每个点 i ( d e g r e e i &gt; x ) i(degree_i&gt;x) i(degreei>x),用堆维护所有它指向 d e g r e e ≤ x degree\leq x degreex的点的边的边权 w w w,和所有它指向的 d e g r e e &gt; x degree&gt;x degree>x的点 j j j f j , 1 − f j , 0 f_{j,1}-f_{j,0} fj,1fj,0 t o t = ∑ j ∈ s o n i , d e g r e e j &gt; x f j , 0 tot=\sum \limits_{j\in son_i,degree_j&gt;x}f_{j,0} tot=jsoni,degreej>xfj,0,取堆中前 d e g r e e i − x degree_i-x degreeix小的加入答案即可。

p.s

注意每次要动态插入 f j , 1 − f j , 0 f_{j,1}-f_{j,0} fj,1fj,0,所以堆不够用了,可以在treap上二分。

若堆中第 k &gt; d e g r e e i − i − j k&gt;degree_i-i-j k>degreeiij小的值 &lt; 0 &lt;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 &lt; m ) k_i=(\sum \limits_{j=1}^ihp_j)\%n(1\leq i&lt;m) ki=(j=1ihpj)%n(1i<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 &lt; m ) sz_i=k_{i+1}-k_i(0\leq i&lt;m) szi=ki+1ki(0i<m),暴力模拟即可。

这样一定可以达到下界 ⌈ ∑ i = 1 m h p i n ⌉ \lceil\frac{\sum\limits_{i=1}^mhp_i}{n}\rceil ni=1mhpi

#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+yz,xy+z,xyz

单独处理每一位 i ( 0 ≤ i &lt; 2 k ) i(0\leq i&lt;2^k) i(0i<2k),假设当前位出现了 a a a x + y + z x+y+z x+y+z, b b b x + y − z x+y-z x+yz, c c c x − y + z x-y+z xy+z, d d d x − y − z x-y-z xyz,则乘积为 ( 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+yz)b(xy+z)c(xyz)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(1in) 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+bcd=cntb
a − b + c − d = c n t c a-b+c-d=cnt_c ab+cd=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 abc+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;
} 
  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值