杂题

Posted by Dispwnl on November 5, 2018

01

题目大意

给定一个$01$串,找满足$0$的数量是$k$倍$1$的数量的最长子串

题解

可以发现一个$1$相当于$k$个$0$,所以记录$0$的数量,把$1$转换成$-k$个$0$,维护两个相同的前缀和的最远距离

代码

# include<iostream>
# include<cstring>
# include<cstdio>
# include<map>
# include<algorithm>
# define LL long long
using namespace std;
const int MAX=1e6+5;
int n,k,_maxn;
char s[MAX];
map<LL,int> ma;
int main()
{
	scanf("%d%d%s",&n,&k,s+1);
	LL ans=0;
	ma[0]=1;
	for(int i=1;i<=n;++i)
	  {
	  	if(s[i]=='0') ++ans;
	  	if(s[i]=='1') ans-=k;
	  	if(ma[ans]) _maxn=max(_maxn,i-ma[ans]+1);
	  	else ma[ans]=i+1;
	  }
	return printf("%d",_maxn),0;
}

biology

题目大意

给定矩阵$a,b$,要求找一条不一定连续的路径$L$,使得路径上的$a>0$且递增,求$\sum_{(x_i,y_i)\in L}b_{i,j}+\mid x_i-x_{i-1}\mid +\mid y_i-y_{i-1}\mid$的最大值

题解

显然排序$a$后可以$O(n^2\times m^2)$转移,发现要是没有绝对值就可以直接$O(n\times m)$转移

把$\mid x_i-x_{i-1}\mid +\mid y_i-y_{i-1}\mid$分情况拆开,维护$4$个值,然后就可以$O(n\times m)$转移了

代码

# include<iostream>
# include<cstring>
# include<cstdio>
# include<algorithm>
# define LL long long
using namespace std;
const int MAX=4e6+5,N=2e3+5;
struct p{
	int x,_x,_y;
	bool operator< (const p &a)
	const{
		return x<a.x;
	}
}qwq[MAX];
int n,m,num;
LL ans;
int b[N][N];
LL f[MAX];
int read()
{
	int x(0);
	char ch=getchar();
	for(;!isdigit(ch);ch=getchar());
	for(;isdigit(ch);x=x*10+ch-48,ch=getchar());
	return x;
}
int main()
{
	n=read(),m=read();
	for(int i=1;i<=n;++i)
	  for(int j=1;j<=m;++j)
	    qwq[++num]=(p){read(),i,j};
	sort(qwq+1,qwq+1+num);
	for(int i=1;i<=n;++i)
	  for(int j=1;j<=m;++j)
	    b[i][j]=read();
	LL mx1=-1e18,mx2=-1e18,mx3=-1e18,mx4=-1e18;
	for(int i=1,L=1;i<=num;++i)
	  if(qwq[i].x)
	  {
	  	if(qwq[i].x!=qwq[i-1].x) for(;L<i;++L)
	  	  if(qwq[L].x) mx1=max(mx1,f[L]-qwq[L]._x-qwq[L]._y),mx2=max(mx2,f[L]-qwq[L]._x+qwq[L]._y),mx3=max(mx3,f[L]+qwq[L]._x-qwq[L]._y),mx4=max(mx4,f[L]+qwq[L]._x+qwq[L]._y);
	  	ans=max(ans,f[i]=max(0ll,max(mx1+qwq[i]._x+qwq[i]._y,max(mx2+qwq[i]._x-qwq[i]._y,max(mx3-qwq[i]._x+qwq[i]._y,mx4-qwq[i]._x-qwq[i]._y))))+b[qwq[i]._x][qwq[i]._y]);
	  }
	return printf("%lld",ans),0;
}

english

题目大意

给定一个序列$a$,求$\sum_{l=1}^{n}\sum_{r=l}^{n}(a_l\oplus a_r)\times max(a_i),l\le i\le r$和$\sum_{l=1}^{n}\sum_{r=l}^{n}[(a_l\oplus a_r)>max(a_i)]\times max(a_i),l\le i\le r$

题解

第一问是很好求的,维护两个单调栈,求出$a_i$左右第一个大于ta的位置,说明这一段区间最大值就是$a_i$,异或就每一位的维护,$num0_l\times num1_r+num1_l\times num0_r$

第二问考虑用$Trie$维护$a_i$二进制每一位,对于第一问求出$i$的区间$[l_i,r_i]$,枚举左右区间中较小的,查询另一半中异或枚举值大于$a_i$的个数

代码

# include<iostream>
# include<cstring>
# include<cstdio>
# include<algorithm>
# define LL long long 
using namespace std;
const int MAX=1e5+5,mod=1e9+7;
int tot,n,op;
struct Trie{
	int siz[MAX<<5];
	int son[MAX<<5][2];
	void ins(int pre,int &i,int x,int k)
	{
		i=++tot,son[i][0]=son[pre][0],son[i][1]=son[pre][1],siz[i]=siz[pre];
		if(k==-1) return void(++siz[i]);
		int qwq=(x>>k)&1;
		ins(son[pre][qwq],son[i][qwq],x,k-1);
		siz[i]=siz[son[i][0]]+siz[son[i][1]];
	}
	LL ask(int pre,int i,int x,int _max,int k)
	{
		if(k==-1) return 0;
		int qwq=(_max>>k)&1,qaq=(x>>k)&1;
		if(qwq) return ask(son[pre][qaq^1],son[i][qaq^1],x,_max,k-1);
		else return (ask(son[pre][qaq],son[i][qaq],x,_max,k-1)+siz[son[i][qaq^1]]-siz[son[pre][qaq^1]]+mod)%mod;
	}
}Tree;
int rt[MAX],a[MAX],st[MAX],lm[MAX],rm[MAX];
LL sum1[21][MAX],sum0[21][MAX];
void count()
{
	for(int i=1;i<=n;++i)
	  for(int j=0;j<=20;++j)
	    sum1[j][i]=sum1[j][i-1]+bool(a[i]&(1<<j)),sum0[j][i]=sum0[j][i-1]+!(a[i]&(1<<j));
}
void work1()
{
	count();
	LL qvq=0,ans=0;
	for(int i=1,l,r;i<=n;++i)
	  {
	  	if(!rm[i]) rm[i]=n+1;
	  	l=lm[i]+1,r=rm[i]-1,qvq=0;
	  	for(int j=0;j<=20;++j)
	  	  {
	  	  	qvq=(qvq+(1ll<<j)*((sum0[j][r]-sum0[j][i])*(sum1[j][i-1]-sum1[j][l-1])%mod+(sum1[j][r]-sum1[j][i])*(sum0[j][i-1]-sum0[j][l-1])%mod)%mod)%mod;
	  	  	if(a[i]&(1<<j)) qvq=(qvq+(1ll<<j)*(sum0[j][r]-sum0[j][l-1])%mod)%mod;
	  	  	else qvq=(qvq+(1ll<<j)*(sum1[j][r]-sum1[j][l-1])%mod)%mod;
		  }
	  	ans=(ans+qvq*a[i]%mod)%mod;
	  }
	printf("%lld\n",ans);
}
void work2()
{
	for(int i=1;i<=n;++i)
	  Tree.ins(rt[i-1],rt[i],a[i],20);
	LL ans=0;
	for(int i=1,l,r;i<=n;++i)
	  {
	  	if(!rm[i]) rm[i]=n+1;
	  	l=lm[i]+1,r=rm[i]-1;
	  	if(i-l>r-i) for(int j=i;j<=r;++j)
	  	  ans=(ans+Tree.ask(rt[l-1],rt[i],a[j],a[i],20)*a[i]%mod)%mod;
	  	else for(int j=l;j<=i;++j)
	  	  ans=(ans+Tree.ask(rt[i-1],rt[r],a[j],a[i],20)*a[i]%mod)%mod;
	  }
	printf("%lld\n",ans);
}
int main()
{
	scanf("%d%d",&n,&op);
	for(int i=1;i<=n;++i)
	  scanf("%d",&a[i]);
	for(int i=1,top=0;i<=n;++i)
	  {
	  	while(top&&a[st[top]]<=a[i]) --top;
		lm[i]=st[top],st[++top]=i;
	  }
	for(int i=n,top=0;i>=1;--i)
	  {
	  	while(top&&a[st[top]]<a[i]) --top;
		rm[i]=st[top],st[++top]=i;
	  }
	if(op&1) work1();
	if(op&2) work2();
	return 0;
}

u

题目大意

给定一个$n\times n$的矩阵,初始矩阵全为$0$,有$q$次修改,每次给一个左上顶点为$(r,c)$、直角边长为$l$的下三角区域加上$s$

求$q$次修改后矩阵的异或和

题解

如果修改的是一段区间的话可以用修改差分数组$O(1)$实现,把直角三角形看成$l$条递增的区间依次修改,复杂度$O(nq+n^2)$

发现如果斜着看,直角三角形斜边上相当于修改区间,所以也可以用修改差分数组的方法修改,竖着同理,复杂度$O(q+n^2)$

代码

# include<iostream>
# include<cstring>
# include<cstdio>
# include<algorithm>
# define LL long long
using namespace std;
const int MAX=1e3+5;
int n,q;
LL ans;
LL s1[MAX][MAX],s2[MAX][MAX];
int main()
{
	scanf("%d%d",&n,&q);
	for(int i=1,r,c,l,s;i<=q;++i)
	  {
	  	scanf("%d%d%d%d",&r,&c,&l,&s),s1[r][c]+=s,s2[r][c+1]-=s;
	  	if(r+l-1<=n) s1[r+l][c]-=s;
	  	if(r+l-1<=n&&c+l<=n) s2[r+l][c+l+1]+=s;
	  }
	for(int i=1;i<=n;++i)
	  for(int j=1;j<=n;++j)
	    s2[i][j]+=s2[i-1][j-1],s1[i][j]+=s1[i-1][j];
	for(int i=1;i<=n;++i)
	  for(int j=1;j<=n;++j)
	    ans^=(s1[i][j]+=s1[i][j-1]+s2[i][j]);
	return printf("%lld",ans),0;
}

v

题目大意

给定一个字符串,里面字符可能是'W''B',要操作$k$次,第$i$次等概率选择一个$[1,n-i+1]$中的数$x$,可以选择从左或者从右开始第$x$个不为空的位置,去掉上面的字符,求最优策略下去除'W'的期望个数

题解

可以用$2$进制表示字符串的状态,因为最大可能是$1«30$,所以小数用数组存,大数用map

然后记忆化搜索即可

代码

# include<iostream>
# include<cstring>
# include<cstdio>
# include<map>
# include<algorithm>
using namespace std;
const int MAX=(1<<18)+1;
int n,K;
char s[51];
double f[MAX][31];
map<int,double> ma[31];
int ss(int S,int k,int qwq)
{
	int L=n-qwq,ll,SS=S,SSS=S;
	ll=k,S>>=ll,SSS>>=(ll-1);
	return (SS-(SSS<<(ll-1)))|(S<<(ll-1));
}
double dfs(int S,int k=0,int qwq=0)
{
	double ans=0;
	if(k==K) return qwq;
	if(S<MAX&&f[S][k]) return f[S][k];
	if(S>=MAX&&ma[k][S]) return ma[k][S];
	int L=n-k;
	for(int i=1;i<=L;++i)
	  {
	  	int RS=ss(S,i,k),LS=ss(S,L-i+1,k),qcq=bool((1<<(i-1))&S),qvq=bool((1<<(L-i))&S);
		if(qvq==qcq) ans+=1/double(L)*max(dfs(LS,k+1,qwq+qvq),dfs(RS,k+1,qwq+qcq));
	  	else if(qvq) ans+=1/double(L)*(dfs(LS,k+1,qwq+qvq));
	  	else ans+=1/double(L)*(dfs(RS,k+1,qwq+qcq));
	  }
	if(S<MAX) f[S][k]=ans;
	else ma[k][S]=ans;
	return ans;
}
int main()
{
	scanf("%d%d%s",&n,&K,s);
	int S=0;
	for(int i=0;i<n;++i)
	  S+=(1<<i)*(s[n-i-1]=='W');
	return printf("%.10lf",dfs(S)),0;
}