2021.09.18 膜你赛

2021.09.18 膜你赛

据说是老吕从三区偷的题。

zero

Description

给出一个长度为 \(n\) 的操作序列。每个位置为 \(\&\),\(|\),^中的一个。\(\&\) 表示按位与,\(|\) 表示按位或,^ 表示按位异或。

定义一个长度为 \(n+1\)\(01\) 数列(即该数列只包含 \(0\)\(1\))是合法的,当且仅当将该操作序列插入这个长度为 \(n+1\) 的数列后,运算结果为 \(1\)

具体的讲,如果给出的操作序列为 &|^,将其插入到序列 \(1010\) 中进行运算:\(1\&0|1\)^\(0=1\)。所以 \(1010\) 就是操作序列 &|^
的一个合法序列。

现在你需要统计对于一个给定的操作序列,有多少个合法序列。
由于答案可能很大,你只需要输出答案对 \(10^9+7\) 取模后的结果。

Solution

挺简单的一道 dp 题。(我能想出来的应该都挺简单)。

\(f_{i,0/1}\) 表示当前进行到第 \(i\) 为,到当前为答案为 \(0/1\) 的方案数。转移也根据三种操作分类就好了。

\(f_{i,0}=(f_{i,0}+f_{i-1,0}\times 2+f_{i-1,1}) |s_i=\&\)
\(f_{i,1}=(f_{i,1}+f_{i-1,1})|s_i=\&\)

\(f_{i,0}=(f_{i,0}+f_{i-1,0})|s_i=|\)
\(f_{i,1}=(f_{i,1}+f_{i-1,0}+f_{i-1,1}\times 2)|s_i=|\)

\(f_{i,0}=(f_{i,0}+f_{i-1,1}+f_{i-1,0})|s_i=\)^
\(f_{i,1}=(f_{i,1}+f_{i-1,0}+f_{i-1,1})|s_i=\)^

Code

/*
* @Author: smyslenny
* @Date:    2021.09.
* @Title:   
* @Main idea:
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <iomanip>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <vector>

#define int long long
#define INF 0x3f3f3f3f
#define orz cout<<"LKP AK IOI\n"
#define MAX(a,b) (a)>(b)?(a):(b)
#define MIN(a,b) (a)>(b)?(a):(b)

using namespace std;
const int mod=1e9+7;
const int M=1e7+5;
int n,f[M][3];
int read()
{
	int x=0,y=1;
	char c=getchar();
	while(c<'0' || c>'9') {if(c=='-') y=0;c=getchar();}
	while(c>='0' && c<='9') { x=x*10+(c^48);c=getchar();}
	return y?x:-x;
}
char s[M];
signed main()
{
//	freopen("zero.in","r",stdin);
//	freopen("zero.out","w",stdout);
	n=read();
	scanf("%s",s+1);
	f[0][1]=1,f[0][0]=1;
	for(int i=1;i<=n;i++)
	{
		if(s[i]=='&')
		{
			f[i][0]=(f[i][0]+(f[i-1][0]*2%mod+f[i-1][1])%mod)%mod;
			f[i][1]=(f[i][1]+f[i-1][1])%mod;
		}
		if(s[i]=='|')
		{
			f[i][0]=(f[i][0]+f[i-1][0])%mod;
			f[i][1]=((f[i][1]+f[i-1][0])%mod+f[i-1][1]*2%mod)%mod;
		}
		if(s[i]=='^')
		{
			f[i][0]=((f[i][0]+f[i-1][1])%mod+f[i-1][0])%mod;
			f[i][1]=((f[i][1]+f[i-1][0])%mod+f[i-1][1])%mod;
		}
	}
	printf("%lld\n",f[n][1]%mod);
	return 0;
}
/*
3
&|^
*/

prime

Description

给定参数 \(L\) \(R\) \(K\),需要计算有多少个 \(y\) 满足,

  1. \(y - k\)\(y + k\) 互质。
  2. \(y - k \in [L, R]\)
  3. \(y + k \in [L, R]\)
    你只需要输出答案对 \(10^9 + 7\) 取模后的结果。

Solution

先推出式子

\(\sum\limits_{i=l}^{R-2k}[\gcd(i,2k)=1]\)

因此我们只要在区间中找出与 \(2k\) 互质的数就好了。将 \(2k\) 进行质因数分解,遵循容斥定理奇加偶减,容斥就好了。

Code

/*
* @Author: smyslenny
* @Date:    2021.09.
* @Title:   
* @Main idea:
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <iomanip>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <vector>

#define int long long
#define INF 0x3f3f3f3f
#define orz cout<<"LKP AK IOI\n"
#define MAX(a,b) (a)>(b)?(a):(b)
#define MIN(a,b) (a)>(b)?(a):(b)

using namespace std;
const int mod=1e9+7;
const int M=1e7+5;
int l,r,k;
int read()
{
	int x=0,y=1;
	char c=getchar();
	while(c<'0' || c>'9') {if(c=='-') y=0;c=getchar();}
	while(c>='0' && c<='9') { x=x*10+(c^48);c=getchar();}
	return y?x:-x;
}

int Ans;
namespace substack1{
	
	int gcd(int a,int b)
	{
		if(b==0) return a;
		return gcd(b,a%b);
	}
	void main()
	{
		for(int i=l+k;i<=r-k;i++)
			if(gcd(i-k,i+k)==1) Ans++;
		printf("%lld\n",Ans);
	}
}

namespace substack2{
	int cnt,prime[M];
	int solve(int x)
	{
		if(x==-1) return 0;
		int sum=0;
		for(int i=0,p=1,f=1;i<1<<cnt;i++,p=1,f=1)
		{
			for(int j=0;j<cnt;j++)
				if(i>>j&1)
					p=p*prime[j],f*=-1;	
			sum=sum+x/p*f;
		}
		return sum;
	}
	
	void main()
	{
		if(l+2*k>r) {printf("0\n");return;}
		k<<=1,r-=k;
		for(int i=2;i<=k;i++)
		{
			if(!(k%i))
			{
				prime[cnt++]=i;
				while(!(k%i))
					k/=i;
			}
		}
		if(k!=1) prime[cnt++]=k;
		printf("%lld\n",solve(r)-solve(l-1));
	}
}

		
signed main()
{
//	freopen("prime.in","r",stdin);
//	freopen("prime.out","w",stdout);
	l=read(),r=read(),k=read();
	substack2::main();
	return 0;
}

mex

Description

给出一个长度为 \(n\) 的序列 \(s\)。定义 \(f(l,r)\) 表示该序列中下标位于区间 \([l,r]\) 的数字构成的集合中没有出现的最小的数字。

你需要计算 \(\sum\limits_{i=1}^{n}\sum\limits_{r=l}^nf(l,r)\)

Solution

先预处理出 \(1\sim i(1\le i\le n)\) 的 mex,还有当前位置的数出现的下一个位置,我们知道,这肯定是单调不降的,然后我们处理左端点变化的情况,我们发现,如果左端点的数字在后面的数列里有的话,那么 mex 就是不变的,否则更新 mex 区间 [i,j] 中比当前值大的值为当前值。

用线段树维护。

Code

/*
  Source: mex
*/
#include<cstdio>
#define int long long
/*----------------------------------------------------------*/
const int B = 1e5 + 7;
const int INF = 0x3f3f3f3f;
/*----------------------------------------------------------*/
template <typename T> inline T Min(T x, T y) {return x < y ? x : y;}
template <typename T> inline T Max(T x, T y) {return x > y ? x : y;}
/*----------------------------------------------------------*/
int n, a[B], mex[B], cnt[B], K, pos[B], pre[B], ans;
/*----------------------------------------------------------*/
inline int read() {
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
void Print(int x) {if(x < 0) putchar('-'), x = -x; if(x > 9) Print(x / 10); putchar(x % 10 ^ 48);}
/*----------------------------------------------------------*/
namespace Seg {
	#define ls(x) x << 1
	#define rs(x) x << 1 | 1
	#define len (t[p].r - t[p].l + 1)
	#define mid (t[p].l + t[p].r >> 1)
	struct node {
		int l, r, sum, max, lzy;
		node() {l = r = sum = max = 0; lzy = -1;}
	} t[B << 2];
	void push_up(int p) {t[p].sum = t[ls(p)].sum + t[rs(p)].sum; t[p].max = Max(t[ls(p)].max, t[rs(p)].max);}
	void build(int p, int l, int r) {
		t[p].l = l; t[p].r = r; if(l == r) {t[p].sum = t[p].max = mex[l]; return ;}
		build(ls(p), l, mid); build(rs(p), mid + 1, r); push_up(p);
	}
	void f(int p, int k) {t[p].lzy = t[p].max = k; t[p].sum = k * len;}
	void push_down(int p) {f(ls(p), t[p].lzy); f(rs(p), t[p].lzy); t[p].lzy = -1;}
	void up_date(int p, int l, int r, int k) {
		if(l <= t[p].l && t[p].r <= r) {f(p, k); return ;}
		if(~t[p].lzy) push_down(p);
		if(l <= mid) up_date(ls(p), l, r, k); if(r > mid) up_date(rs(p), l, r, k);
		push_up(p);
	}
	int query(int p, int k) {
		if(t[p].l == t[p].r) return t[p].l;
		if(k < t[ls(p)].max) return query(ls(p), k); else return query(rs(p), k);
	}
}
void Main() {
	n = read(); mex[n + 1] = INF; pre[0] = n + 1;
	for(int i = 1; i ^ n + 1; ++i) a[i] = read(), cnt[a[i]]++, pre[i] = n + 1;
	for(int i = 0; i ^ n + 1; ++i) if(!cnt[i]) {K = i; break;}
	for(int i = n; i; --i)
	{
		mex[i] = K; if(!--cnt[a[i]]) K = Min(K, a[i]);
		pos[i] = pre[a[i]]; pre[a[i]] = i;
	}
	Seg::build(1, 1, n); ans = Seg::t[1].sum;
	for(int i = 1; i ^ n + 1; ++i)
	{
		Seg::up_date(1, i, i, 0);
		if(Seg::t[1].max > a[i])
		{
			int x = Seg::query(1, a[i]);
			if(x < pos[i]) Seg::up_date(1, x, pos[i] - 1, a[i]);
		}
		ans += Seg::t[1].sum;
	}
	Print(ans);
}
/*----------------------------------------------------------*/
signed main() {Main(); return 0;}	

这显然是BS的代码,我没写。

本欲起身离红尘,奈何影子落人间。
原文地址:https://www.cnblogs.com/jcgf/p/15310790.html