2023.8.28

A

长为 \(n=2^k-1\) 的纸条,编号为 \([0,n-1)\),将纸条对折 \(k\) 次(每次将右边翻转至左边下面),记形成的序列为 \(\{a_n\}\).

\(m\) 次询问,给定 \(l,r\) 求解:

\[F(l,r)=a_l+a_{l+1}\oplus a_{l+2}+a_{l+3}\oplus a_{l+4}+\dots a_r \]

\(l\) 为偶数,那么先计算 \(+\),否则先计算 \(\oplus\).

为减少读入量,进行如下操作:

  • \(l,r\) 为初始给定的值,\(h=0\).

  • 执行 \(m\) 次,令:

\[h\leftarrow (l\oplus r\oplus h\oplus F(l,r)+c)\operatorname{mod}\rm{(1e9+7)} \]

\[l\leftarrow (l\oplus a\oplus h)\operatorname{mod}(n+1)\operatorname{mod}n \]

\[r\leftarrow (r\oplus b\oplus h)\operatorname{mod}(n-l)+l \]

问最后 \(h\) 的值。

\(0\le k\le 30\)\(1\le m\le 5\times 10^7\)\(0\le a,b,c< 2^{30}\).

看下 \(k=3\) 是怎么折的。

\[\begin{bmatrix}0&1&2&3&4&5&6&7\end{bmatrix} \]

\[\Longrightarrow\begin{bmatrix}0&1&2&3\\7&6&5&4\end{bmatrix} \]

\[\Longrightarrow\begin{bmatrix}0&1\\7&6\\4&5\\3&2\end{bmatrix} \]

\[\Longrightarrow\begin{bmatrix}0\\7\\4\\3\\2\\5\\6\\1\end{bmatrix} \]

\(\{a_n\}=\{0,7,4,3,2,5,6,1\}\).

直接观察可以发现每两项的和都是 \(7\),不难猜到每对偶位和奇位的和是 \(2^k-1\).

然后再观察这个 \(F(l,r)\) 在做什么。

\(l\) 为偶数:

\[(a_l+a_{l+1})\oplus(a_{l+2}+a_{l+3})\oplus\dots a_r \]

那么会产生 \(\lfloor\frac{r-l}{2}\rfloor\)\(2^k-1\),再考虑 \(r\) 位的值。

\(l\) 为奇数:

\[a_l+(a_{l+1}\oplus a_{l+2})+\dots a_r \]

会产生 \(\lfloor\frac{r-l-1}{2}\rfloor\)\(2^k-1\),加上 \(a_l\) 的值,再考虑 \(a_r\).

再来看怎么求 \(a_x\),容易手推出来一个单次 \(O(k)\) 的做法,但是不够理想。

我们需要一个单次 \(O(1)\) 的做法。

将折纸过程对半预处理,预处理出纸条折叠 \(\lfloor\frac{k}{2}\rfloor\) 次时每一层的数字区间,和纸叠展开 \(\lceil\frac{k}{2}\rceil\) 次后位置的对应关系。

对应关系仅和所求位置的前 \(\lceil\frac{k}{2}\rceil\) 位有关。

时间复杂度 \(O(2^{\lceil\frac{k}{2}\rceil})+m\).

这些东西的处理就看起来比较玄学了。

#include<bits/stdc++.h>
#define ll long long
#define P 1000000007
using namespace std;
int read(){
	int x=0,w=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
	while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*w;
}
int k,n,m,a,b,c;
int num[1<<15][2],num2[1<<15][2];
int getpos(int x){
	int res=num2[x>>15][1];
	x^=num2[x>>15][0];
	return num[x][0]<=num[x][1]?num[x][0]+res:num[x][0]-res;
}
ll query(int l,int r){
	if(!(l&1))return (((r-l+1)/2)&1?(n-1):0)^((r&1)?0:getpos(r));
	return 1ll*((r-l)/2)*(n-1)+getpos(l)+((r&1)?0:getpos(r));
}
int main(){
	k=read(),m=read(),n=(1<<k);
	num[0][1]=(1<<k)-1;
	for(int i=0;i<=min(k-1,14);i++)
		for(int j=0;j<(1<<i);j++){
			num[(1<<i+1)-j-1][0]=num[j][1];
			num[(1<<i+1)-j-1][1]=num[j][1]-(num[j][1]-num[j][0])/2;
			num[j][1]=num[j][0]+(num[j][1]-num[j][0])/2;
		}
	for(int i=0;i<(1<<max(k-15,0));i++)
		for(int j=k-1,p=i<<15;j>=15;j--)
			if(p>=(1<<j)){
				p^=(1<<j+1)-1;
				num2[i][0]^=(1<<j+1)-1;
				num2[i][1]^=(1<<k-j)-1;
			}
	int l=read(),r=read(),h=0;
	a=read(),b=read(),c=read();
	for(int i=1;i<=m;i++){
		h=(((1ll*query(l,r))^l^r^h)+c)%P;
		l=(l^a^h)%(n+1)%n;
		r=(r^b^h)%(n-l)+l;
	}
	printf("%d\n",h);
	
	return 0;
}

B

签到题。

\[\sum_{i=0}^{n}(a^i+b^i+c^i)^k \]

\(998244353\) 取模的值。

\(1\le n\le 10^9\)\(0\le k\le 1000\).

用多重二项式定理展开式子:

\[\sum_{i=0}^{n}\sum_{x,y,z\in\mathbb N,x+y+z=k}\binom{k}{x,y,z}a^{ix}b^{iy}c^{iz} \]

\[k!\cdot\sum_{x,y,z\in \mathbb N,x+y+z=k}\frac{1}{x!\cdot y!\cdot z!}\sum_{i=0}^{n}(a^xb^yc^z)^i \]

枚举 \(x,y\) 做等比数列求和。里面的式子为 \(1\) 时特判即可。

时间复杂度 \(O(k^2\log n)\).

怎么看起来处理 \(1\) 的时候我的好像假了。但是的确时过了的。

#include<bits/stdc++.h>
#define N 1010
#define P 998244353
using namespace std;
int read(){
	int x=0,w=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
	while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*w;
}
int qpow(int k,int b){
	int ret=1;
	while(b){
		if(b&1)ret=1ll*ret*k%P;
		k=1ll*k*k%P,b>>=1;
	}
	return ret;
}
int n,k,a,b,c;
int fac[N],ifac[N],pa[N],pb[N],pc[N];
int main(){
	n=read(),k=read(),a=read(),b=read(),c=read();
	fac[0]=ifac[0]=pa[0]=pb[0]=pc[0]=1;
	for(int i=1;i<=k;i++){
		fac[i]=1ll*fac[i-1]*i%P;
		pa[i]=1ll*pa[i-1]*a%P,pb[i]=1ll*pb[i-1]*b%P,pc[i]=1ll*pc[i-1]*c%P;
	}
	ifac[k]=qpow(fac[k],P-2);
	for(int i=k-1;i;i--)
		ifac[i]=1ll*ifac[i+1]*(i+1)%P;
	int ans=0,cur,tp;
	for(int x=0;x<=k;x++)
		for(int y=0,z;x+y<=k;y++){
			z=k-x-y;
			cur=1ll*ifac[x]*ifac[y]%P*ifac[z]%P;
			tp=1ll*pa[x]*pb[y]%P*pc[z]%P;
			int check=(a==1?x:0)+(b==1?y:0)+(c==1?z:0);
			if(check==k)cur=1ll*cur*(n+1)%P;
			else cur=1ll*cur*(P+qpow(tp,n+1)-1)%P*qpow((P+tp-1)%P,P-2)%P;
			(ans+=cur)%=P;
		}
	printf("%d\n",1ll*ans*fac[k]%P);
	
	return 0;
}

C

将数列 \(\{a_n\}\) 划分为若干段,使得相邻两端的最小值的差的绝对值 \(\ge E\).

问总方案数对 \(\rm 1e9+7\) 取模的值。

\(n\le 5\times 10^5\)\(1\le a_i,E\le 10^9\).

\(f_i\)\(i\) 前面分好,\(i\) 为当前段的最小值的答案(该段可以未闭合)。

\(L_i\) 为比 \(i\) 小的上一个数。考虑转移。

上一段的最小值可能为 \([L[i],u)\),记为 \(j\).

\(f_j\) 无论切在 \([j+1,i]\) 的哪里都可以,即 \(f_j\)\(i\) 的贡献为 \(f_j\times(k-j)\),其中 \(k\) 为下一个比 \(j\) 小的数。

那么 \(f_j\) 可以对 \([j,k)\) 作贡献,单调队列并维护版本和即可。

时间复杂度 \(O(n\log n)\).

#include<bits/stdc++.h>
#define N 500010
#define P 1000000007
#define V (int)1e9
using namespace std;
int read(){
	int x=0,w=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
	while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*w;
}
int rt[N],tot;
struct Tree{
	int ls,rs,c0,c1;
	#define ls(x) t[x].ls
	#define rs(x) t[x].rs
	#define c0(x) t[x].c0
	#define c1(x) t[x].c1
}t[N<<6];
void modify(int &p,int l,int r,int x,int c0,int c1){
	t[++tot]=t[p],p=tot;
	(c0(p)+=c0)%=P,(c1(p)+=c1)%=P;
	if(l==r)return;
	int mid=(l+r)>>1;
	if(x<=mid)modify(ls(p),l,mid,x,c0,c1);
	else modify(rs(p),mid+1,r,x,c0,c1);
}
int query(int p,int l,int r,int L,int R,int opt){
	if(!p)return 0;
	if(L<=l&&r<=R)return !opt?c0(p):c1(p);
	int mid=(l+r)>>1,ret=0;
	if(L<=mid)ret=query(ls(p),l,mid,L,R,opt);
	if(R>mid)(ret+=query(rs(p),mid+1,r,L,R,opt))%=P;
	return ret;
}
int n,a[N],E;
int st[N],top,L[N];
int f[N];
int main(){
	n=read(),E=read();
	for(int i=1;i<=n;i++)a[i]=read();
	for(int i=1;i<=n;i++){
		while(top&&a[i]<=a[st[top]])top--;
		L[i]=st[top],st[++top]=i;
	}
	top=0;
	for(int u=1;u<=n;u++){
		if(!L[u])f[u]=1;
		int i=u,c0,c1,sum;
		c0=(c0(rt[i-1])-query(rt[i-1],1,V,max(a[u]-E+1,1),min(a[u]+E-1,V),0)+P)%P;
		c1=(c1(rt[i-1])-query(rt[i-1],1,V,max(a[u]-E+1,1),min(a[u]+E-1,V),1)+P)%P;
		sum=(1ll*i*c0%P-c1+P)%P,(f[u]+=sum)%=P;
		i=L[u];
		c0=(c0(rt[i-1])-query(rt[i-1],1,V,max(a[u]-E+1,1),min(a[u]+E-1,V),0)+P)%P;
		c1=(c1(rt[i-1])-query(rt[i-1],1,V,max(a[u]-E+1,1),min(a[u]+E-1,V),1)+P)%P;
		sum=(1ll*i*c0%P-c1+P)%P,(f[u]+=P-sum)%=P;
		rt[u]=rt[u-1];
		while(top&&a[u]<=a[st[top]]){
			modify(rt[u],1,V,a[st[top]],P-f[st[top]],1ll*(P-f[st[top]])*u%P);
			top--;
		}
		st[++top]=u;
		modify(rt[u],1,V,a[u],f[u],1ll*f[u]*u%P);
	}
	int ans=0,mn=V+1;
	for(int i=n;i;i--)
		if(mn>a[i])(ans+=f[i])%=P,mn=a[i];
	printf("%d\n",ans);
	
	return 0;
}

D

给出 \(m\) 条网线,对于每条网线有一个数 \(k_i\)\(k_i\)\([1,n]\) 中的数,表示该网线可以选取其中的两个子机相连(也可以不操作)。

一台子机至多连接一条网线,问最多使用的网线条数。

\(n\le 8000\)\(m\le 4400\)\(1\le k_i\le \min(40,n)\).

数据随机。

令每个子机对应一个点,每条网线对应两个点 \(x,y\),在两点间连一条边,\(x\)\(y\) 均向能连接的电脑连边。

那么应有答案=匹配数-网线数。跑一般图最大匹配即可。

在火车头加持下常数很小。

#include<bits/stdc++.h>
#define N 16810
using namespace std;
int read(){
	int x=0,w=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
	while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*w;
}
int n,m,ans;
int node;
vector<int>e[N];
void add(int u,int v){
	e[u].push_back(v),e[v].push_back(u);
}
int f[N],match[N];
int tag[N],tim,col[N],pre[N];
queue<int>q;
int find(int x){
	return x==f[x]?x:f[x]=find(f[x]);
}
int lca(int x,int y){
	tim++;
	while(true){
		if(x){
			x=find(x);
			if(tag[x]==tim)return x;
			tag[x]=tim,x=pre[match[x]];
		}
		swap(x,y);
	}
	return -1;
}
void blossom(int x,int y,int w){
	while(find(x)!=w){
		pre[x]=y,y=match[x];
		col[y]=1,q.push(y);
		if(find(x)==x)f[x]=w;
		if(find(y)==y)f[y]=w;
		x=pre[y];
	}
}
bool bfs(int s){
	if((ans+1)*2>node)return false;
	for(int i=1;i<=node;i++)f[i]=i,col[i]=pre[i]=0;
	while(!q.empty())q.pop();
	q.push(s),col[s]=1;
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int v:e[u]){
			if(find(u)==find(v)||col[v]==2)continue;
			if(!col[v]){
				col[v]=2,pre[v]=u;
				if(!match[v]){
					int x=v,y,z;
					while(x){
						y=pre[x],z=match[y];
						match[x]=y,match[y]=x;
						x=z;
					}
					return true;
				}
				col[match[v]]=1,q.push(match[v]);
			}
			else{
				int w=lca(u,v);
				blossom(u,v,w),blossom(v,u,w);
			}
		}
	}
	return false;
}
int main(){
	n=read(),m=read();
	node=m*2+n;
	for(int i=1,k,u;i<=m;i++){
		k=read(),add(i,m+i);
		while(k--)
			u=read(),add(i,m*2+u),add(m+i,m*2+u);
	}
	for(int i=1;i<=node;i++)
		if(!match[i])ans+=bfs(i);
	printf("%d\n",ans-m);
	
	return 0;
}
posted @ 2023-08-28 20:22  SError  阅读(58)  评论(0编辑  收藏  举报