Codeforces Round #757 (Div. 2)


C. Divan and bitwise operations

一天,Divan 研究了一个由 \(n\) 个非负整数构成的序列,\(a_1,a_2,...a_n\) 他选出了这个序列 \(a\) 的所有非空子序列,计算出所有子序列的异或 xor 值,然后把这些子序列的异或值加起来,得到了这个序列的 conziness 值。

\[conziness =\sum(\sum _1 ^{size} a_{k_1} \oplus a_{k_2}...\oplus a_{k_{size}}), size\leq n \]

一个子序列的定义是从原序列中任意删除几个数(零,一,…,所有),剩下的元素按照原来的顺序写在一起,得到一个新的序列,即原序列的子序列。

Divan 对他的研究感到非常骄傲,但是他现在不小心把序列 \(a\) 给弄丢了,当然 conziness 值也弄丢了!但是,Divan 知道 \(m\) 个子串的同或 or值,并且,原序列的每个元素都至少在这 \(m\) 个子串中出现过一次。

Divan 想让你帮忙找出 \(a\) 序列的 conziness 值,保证存在至少一种方案,如果有多种可能,任意输出一种即可。

由于答案可能特别大,请将最后的答案对 \(10^9 + 7\) 取模。

多测,有 \(t\) 组数据 .

\(1\leq t\leq 10^3,1\leq n,m\leq 2\cdot 10^5,1\leq l\leq r\leq n,0\leq x<2^{30}-1,\sum n\leq 2\cdot 10^5,\sum m\leq 2\cdot 10^5\)

sol1

发现要根据or的值重新构建 \(a\) .

分析 or 的性质,发现,or 中为 \(1\) 的一位中至少有一个 \(1\) ,为 \(0\) 中不能有 \(1\) .

因为保证了存在答案,所以,考虑按区间大小从小往大填,把区间从 \(l\)\(r\) 中没有赋值的都赋值成 \(x\) . 这样一定是有解的,但是这个方法不是很好实现,鉴于是才是 div2 的 c ,考虑一些更好写的方法 .

考虑将区间 \([l,r]\) 按照 \(r\) 从小到大,\(l\) 从大到小的顺序排序 . 考虑枚举位数,从 \(1\)\(n\) . 用一个 set 维护包含 \(l\) 的限制条件,其中 \((r,x)\) 从小到大排序,考虑,对于当前 \(i\) ,首先要弹出 \(s\)\(r 的元素,然后找到 \(s\) 中第一个元素,令 \(a_i=x\) . 否则,令 \(a_i=0\) . 然后在 \(s\) 插入 \(l=i\)\((r,x)\) .

因此,就可以得到一个合法的 \(a_i\) .

接下来,用 \(cnt(i,k,0/1)\) 表示,到了第 \(i\) 位,二进制第 \(k\) 位为 \(0/1\) 的序列的个数 . 稍微 \(dp\) 一下即可 .

时间复杂度 : \(O(n\log v+m\log m)\)

空间复杂度 : \(O(n\log v)\)

code
#include
using namespace std;
const int mod=1e9+7;
int t;
int n,m;
vector >v[200010];
int b[200010];
set >s;
int cnt[200010][32][2];
inline void upd(int&x,int y){x=(x+y)%mod;}
void solve(){
	cin>>n>>m;
	for(int i=0;i>l>>r>>x;
		l--;r--;
		v[l].push_back(make_pair(r,x));
	}
	for(int i=0;ifirstsecond;
		else b[i]=0;
	}
	for(int i=0;i<=n;i++)memset(cnt[i],0,sizeof(cnt[i]));
	for(int i=0;i>t;
	while(t--){
		solve();
	}
	return 0;
}
/*inline? ll or int? size? min max?*/
sol2

考虑求出了 \(b_i\) 数组,其中 \(b_i\) 中第 \(j\) 位为 \(1\) 的有 \(k\) 个 .

考虑子序列中 xor 起来最终第 \(j\) 位为 \(1\) 的子序列数量 .

\(k=0\) 时,必然为 \(0\) .

否则,考虑前把一个第 \(j\) 位为 \(1\) 的数换到末尾,这样不影响结果 . 前 \(n-1\) 个数随便选 ,一共是 \(2^{n-1}\) 个子序列 . 如果前面 xor 为 \(0\) ,那么,最后一个选 ; 否则,最后一个不选 . 都只有一种选择,所以,一共是 \(2^{n-1}\) 个选法 .

所以,只需要对 \(b_i\) 的 or 乘上 \(2^{n-1}\) .

但是 \(b_i\) 的 or ,必然是 \(x\) 的 or . 一定不存在 \(b_i\) 中某一位为 \(1\)\(x\) 中不为 \(1\) .

那么,答案即是 \(x\) 的 or 乘上 \(2^{n-1}\) .

时间复杂度 : \(O(n+m)\)

空间复杂度 : \(O(1)\)

code

code

#include
using namespace std;
const int mod=1e9+7;
int t;
int n,m;
inline void upd(int&x,int y){x=(x+y)%mod;}
inline int ksm(int x,int k){
	if(k==0)return 1;
	int res=ksm(x,k>>1);
	res=1ll*res*res%mod;
	if(k&1)res=1ll*res*x%mod;
	return res;
}
void solve(){
	cin>>n>>m;
	int ans=0;
	for(int i=0;i>l>>r>>x;
		ans|=x;
	}
	ans%=mod;
	cout<<1ll*ans*ksm(2,n-1)%mod<>t;
	while(t--){
		solve();
	}
	return 0;
}
/*inline? ll or int? size? min max?*/
D2. Divan and Kostomuksha (hard version)

给定一个序列 \(a\) ,定义其权值为:

\[\sum_{i=1}^n\gcd(a_1,a_2,...,a_i) \]

现在你可以重排 \(a\) ,求这个权值的最大值。

\(1\leq n\leq 10^5,1\leq a_i\leq 2\cdot 10^7\)

\(dp(i)\) 表示,从前往后 \(\gcd\)\(i\) 时的最大和 . 用 \(cnt(i)\) 表示 \(a_i\) 中包含 \(i\) 的数的个数 .

转移时枚举 \(i\) 中的所有质因数 \(k\) 进行转移,

\[dp(\frac{i}{j})=dp(i)+(cnt(j)-cnt(i))j \]

最后 \(dp(1)\) 即为答案 .

但是,如果做到用最少的复杂度得到 \(j\) 呢 ?

考虑对 \(2\times 10^7\) 的数都维护一个 \(nxt(i)\) 表示 \(i\) 的最小质数 . 此时,只需要循环求一下 \(i/nxt(i)\)\(nxt(i)\) ,知道 \(i=1\) 时就可以得到所有质数.

\(nxt(i)\) 需要的时间为 \(O(v\log v)\) 的,但是听说这个时间复杂度是 \(O(\log v\log v)\) 的.

时间复杂度 : \(O(v\log v\log v)\)

空间复杂度 : \(O(v)\)

code
#include
using namespace std;
int n;
int a[100010];
bool vis[20000010];
int cnt[20000010];
long long dp[20000010];
bool pri[20000010];
int nxt[20000010];
int main(){
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	cin>>n;
	for(int i=0;i>a[i];
	for(int i=0;i20000000)continue;
		for(int j=i*i;j<=20000000;j+=i){
			pri[j]=false;
			if(!nxt[j])nxt[j]=i;
		}
	}
	for(int i=1;i<=20000000;i++)if(vis[i])dp[i]=1ll*i*cnt[i];
	long long ans=0;
	for(int i=20000000;i>=1;i--)if(vis[i]){
		ans=max(ans,dp[i]);
		int tmp=i;
		while(nxt[tmp]!=0){
			int x=i/nxt[tmp];
			dp[x]=max(dp[x],dp[i]+1ll*x*(cnt[x]-cnt[i]));
			tmp=tmp/nxt[tmp];
		}
	}
	cout<
E. Divan and a Cottage

给定 \(n\) 天的气温 \(T_i\) ,设第 \(i\) 天温度为 \(P\) ,则第 \(i+1\) 天的温度为:

  • \(P+1\) \(( P
  • \(P-1( P>T_i)\)
  • \(P ( P=T_i )\)

对第 \(i\) 天有 \(k_i\) 个询问,问若第 \(0\) 天的温度为 \(x\) ,那么第 \(i\) 天的温度是多少。

强制在线 .

\(1\leq n\leq 2\cdot 10^5,0\leq T_i\leq 10^9,0\leq k_i\leq 2\cdot 10^5,0\leq x_i'\leq 10^9,\sum k\leq 2\cdot 10^5\)

考虑如果在第 \(i\) 天,温度 \(a,b\) 满足 \(a\leq b\) 那么,在第 \(i+1\) 天,也会满足 \(a'\leq b'\) .

因此,值域是不连续的,但是相对顺序不会发生变化 .

考虑用动态开店线段树,线段树上维护 \([0,\inf]\) 的值域 . 每个点维护初始温度为 \(i\) 的情况下的最终温度 . 当遇到新的一天时,将当前温度在 \([0,t_i-1]\) 的区间 \(+1\) ,在 \([t_i+1,\inf]\) 的区间 \(-1\) .

( 嘿嘿,第一次写动态开点线段树 )

时间复杂度 : \(O(n\log v)\)

空间复杂度 : \(O(n\log v)\)

code
#include
using namespace std;
const int inf=1e9;
int n,cnt=0;
class node{public:int ls,rs,mn,mx,tag;}ts[50000010];
inline new_node(int mn,int mx){
	++cnt;
	ts[cnt].ls=ts[cnt].rs=0;
	ts[cnt].mn=mn;
	ts[cnt].mx=mx;
	ts[cnt].tag=0;
	return cnt;
}
inline void pd(int x){
	if(ts[x].tag==0)return;
	if(ts[x].ls){
		ts[ts[x].ls].mn+=ts[x].tag;
		ts[ts[x].ls].mx+=ts[x].tag;
		ts[ts[x].ls].tag+=ts[x].tag;
	}
	if(ts[x].rs){
		ts[ts[x].rs].mn+=ts[x].tag;
		ts[ts[x].rs].mx+=ts[x].tag;
		ts[ts[x].rs].tag+=ts[x].tag;
	}
	ts[x].tag=0;
}
void upd1(int x,int l,int r,int k){
	if(ts[x].mn>k)return;
	if(ts[x].mx>1;
	if(!ts[x].ls){
		int tmp=new_node(l,mid);
		ts[x].ls=tmp;
		ts[tmp].mn+=ts[x].mn-l;
		ts[tmp].mx+=ts[x].mx-r;
	}
	if(!ts[x].rs){
		int tmp=new_node(mid+1,r);
		ts[x].rs=tmp;
		ts[tmp].mn+=ts[x].mn-l;
		ts[tmp].mx+=ts[x].mx-r;
	}
	if(ts[ts[x].ls].mnk){
		pd(x);
		ts[x].mn--;
		ts[x].mx--;
		ts[x].tag--;
		return;
	}
	if(l==r)return;
	pd(x);
	int mid=(l+r)>>1;
	if(!ts[x].ls){
		int tmp=new_node(l,mid);
		ts[x].ls=tmp;
		ts[tmp].mn+=ts[x].mn-l;
		ts[tmp].mx+=ts[x].mx-r;
	}
	if(!ts[x].rs){
		int tmp=new_node(mid+1,r);
		ts[x].rs=tmp;
		ts[tmp].mn+=ts[x].mn-l;
		ts[tmp].mx+=ts[x].mx-r;
	}
	if(ts[ts[x].rs].mx>k)upd2(ts[x].rs,mid+1,r,k);
	if(ts[ts[x].ls].mx>k)upd2(ts[x].ls,l,mid,k);
	ts[x].mn=ts[ts[x].ls].mn;
	ts[x].mx=ts[ts[x].rs].mx;
}
int qry(int x,int l,int r,int k){
	if((!ts[x].ls)&&(!ts[x].rs))return k+ts[x].mn-l;
	pd(x);
	int mid=(l+r)>>1;
	if(k<=mid){
		if(!ts[x].ls)return k+ts[x].mn-l;
		else return qry(ts[x].ls,l,mid,k);
	}
	else{
		if(!ts[x].rs)return k+ts[x].mx-r;
		else return qry(ts[x].rs,mid+1,r,k);
	}
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	int rt=new_node(0,inf);
	cin>>n;
	int ans=0;
	for(int i=0;i>t;
		upd1(rt,0,inf,t);
		upd2(rt,0,inf,t);
		int k;cin>>k;
		for(int j=0;j>x;
			x=(x+ans)%(inf+1);
			ans=qry(rt,0,inf,x);
			cout<

大自闭,这场 vp只对了 ab,c 看错提议,d2 因为在求质数,遇到 \(i^2>2\cdot 10^7\) 的时候 break 了,导致后面质数的 \(nxt(i)=0\) ,看了很久没有看出来 ,自闭了 .