DTOJ-2022-11-14-测试-题解

测试成果

\(100+100+0+92=292\)

还行

A 签到题

题目链接

DTOJ P6363

题面大意

Diana 有一个函数 \(f(x)\) 表示 \(x\) 十进制下的各位之和,例如 \(f(233) = 2 + 3 + 3 = 8\)Diana 还有一个整数 \(n\),她告诉你有两个正整数. \(A,B\) 满足 \(A + B = n\),你需要求出 \(f(A) +f(B)\) 的最小值。

题解

显然如果进位了是不优的,如果 \(n\ne 10^k\) 一定可以构造出不进位的 \(A,B\) ,这时候答案 \(=f(n)\).

否则答案 \(=10\).

代码

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e5+5;
int T;
char s[N];
int main()
{
	scanf("%d",&T);
	while(T--)
	{
		scanf("%s",s+1);
		int n=strlen(s+1),res=0;
		for(int i=1; i<=n; i++) res+=s[i]-'0';
		printf("%d\n",(res==1)?10:res);
	}
	return 0;
}

B 大根堆

题面链接

DTOJ P6364

题面大意

\(1\sim n\) 大根堆,\(i\) 儿子的数量 \(\le d_i\),求方案数,儿子有顺序

\(1 \le T \le 5\)\(1 \le n \le 5000\)\(0 \le d_i < i\)

题解

考虑 \(1\sim n-1\) 的父亲 \(fa_i\)

显然 \(i< fa_i\le n\)

我们考虑 \(fa_i=2,\dots,n\) 依次往 \(\{fa_i\}\) 中填

\(f_{i,j}\) 表示填了 \(fa_i=2,\dots, i\) ,总共填了 \(j\) 个位置的方案数.

转移的话就是

\[f_{i,j}=\sum_{max(0,j-d[i])\le k \le j}f_{i-1,k}\cdot \binom{i-1-k}{j-k}\cdot (j-k)! \]

新填了 \(j-k\)\(fa_x=i\),有 \(i>x\) 也就是说空位有 \(i-1-k\) 个,那么填法就是 \(\binom{i-1-k}{j-k}\) 个,注意到儿子有顺序所以要乘 \((j-k)!\)

调教一下式子

\[\begin{align*} f_{i,j}&=\sum_{max(0,j-d[i])\le k \le j}f_{i-1,k}\cdot \frac{(i-1-k)!}{(i-1-j)!}\\ &= \frac{1}{(i-1-j)!}\sum_{max(0,j-d_i)\le k \le j}f_{i-1,k}\cdot(i-1-k)!\\ 记\ g_{i,j}&=\sum_{k \le j}f_{i-1,k}\cdot(i-1-k)!\\ 则\ f_{i,j}&=\frac{1}{(i-1-j)!}\cdot (g_j-g_{j-d_i-1}) \end{align*} \]

然后就可以 \(O(n^2)\)

(前缀和优化是好东西)

代码

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 5005, P = 998244353;
int T,n,d[N],f[N][N],g[N][N],fc[N],fci[N];
int ksm(int a, int b)
{
	int res=1;
	for(; b; b>>=1,a=(ll)a*a%P) if(b&1) res=(ll)a*res%P;
	return res;
}
void init()
{
	fc[0]=1;
	for(int i=1; i<N; i++) fc[i]=(ll)fc[i-1]*i%P;
	fci[N-1]=ksm(fc[N-1],P-2);
	for(int i=N-1; i; i--) fci[i-1]=(ll)fci[i]*i%P; //预处理阶乘哦
}
int C(int n, int m)
{
	if(n<0 or m<0 or n<m) return 0;
	return (ll)fc[n]*fci[m]%P*fci[n-m]%P;
}

int main()
{
	scanf("%d",&T);
	init();
	//for(int i=0; i<=10; i++) printf("%d %d\n",fc[i],fci[i]);
	while(T--)
	{
		scanf("%d",&n);
		memset(f,0,sizeof(f));
		for(int i=1; i<=n; i++) scanf("%d",&d[i]);
		f[1][0]=g[1][0]=g[1][1]=1;
		/*
		O(n^3) 暴力版本
		for(int i=2; i<=n; i++)
			for(int j=0; j<i; j++)
				for(int k=0; k<=j; k++)
					if(j-k<=d[i]) f[i][j]=(f[i][j]+(ll)f[i-1][k]*C(i-k-1,j-k)%P*fc[j-k]%P)%P;*/
		for(int i=2; i<=n; i++)
		{
			for(int j=0; j<i; j++)
			{
				int res=g[i-1][j];
				if(j-d[i]>0) res=(res-g[i-1][j-d[i]-1]+P)%P;
				f[i][j]=(ll)fci[i-1-j]*res%P;
				g[i][j]=(ll)fc[i-j]*f[i][j]%P;
				if(j) g[i][j]=(g[i][j]+g[i][j-1])%P;
			}
			g[i][i]=g[i][i-1]; //记得加这个要不然转移会出问题(
		}
		printf("%d\n",f[n][n-1]);
	}
	return 0;
}

C 抽卡

题面链接

portal

题面大意

\(n\) 个物品,每个时刻都会恰好出现一个物品,物品 \(i\) 会以
\(p_i\) 的概率出现.

记物品 \(i\) 的第一次出现时间为 \(t_i\),那么定义出现时间的平均数 \(\overline{t}\) 和方差 \(\sigma^2\) 为:

\[\overline{t}=\dfrac{1}{n}\sum_{i=1}^n t_i\quad \quad \sigma^2=\dfrac{1}{n}\sum_{i=1}^n{(t_i-\overline{t})^2} \]

\[ \]

\(E(\overline{t})\), \(E(\sigma^2\)) ,对 \(998244353\) 取模.

题解

纯纯概率期望推式子题,可惜我不会(

先写题解再写题(

1.\(E(\overline{t})\)

首先我们要\(E(\frac{1}{n}\sum t_i)\)

\[\begin{align*} E\left(\frac{1}{n}\sum t_i\right) &= \frac{1}{n} E\left(\sum t_i\right) \\ &= \frac{1}{n} \sum E(t_i) \end{align*} \]

期望的线性性是个好东西! ̄ω ̄=

所以我们就要\(E(t_i)\) :直接考虑每种事件的概率啊!

\[\begin{align*} E(t_i)&= p_i\cdot 1 + p_i(1-p_i)\cdot 2 + p_i(1-p_i)^2\cdot 3 +\dots\\ &= p_i(1+2(1-p_i)+3(1-p_i)^2+\dots)\\ \\ \because S&=1+2q+3q^2+\dots \\ \Rightarrow S&=\frac{1+q+q^2+\dots}{1-q}=\frac{1}{(1-q)^2}\\ \therefore E(t_i)&=p_i\cdot\frac{1}{p_i^2}=\frac{1}{p_i} \end{align*} \]

代回去!!

\[\begin{align*} E\left(\frac{1}{n}\sum t_i\right) &= \frac{1}{n} \sum E(t_i)\\ &= \frac{1}{n} \sum \frac{1}{p_i} \end{align*} \]

诶 20 分不就到手了吗(!( ̄︶ ̄)\(\uparrow\)

2.\(E(\sigma^2)\)

来来来我们来推方差

注意一个小结论

\[\frac{1}{n}\sum_{i=1}^n{(t_i-\overline{t})^2}= \frac{1}{n}\sum_{i=1}^n t_i^2 - \left(\frac{1}{n}\sum_{i=1}^n t_i\right)^2 \]

\[\begin{align*} E(\sigma^2)&=E\left(\frac{1}{n}\sum_{i=1}^n{(t_i-\overline{t})^2}\right)\\ &=E\left(\frac{1}{n}\sum t_i^2\right) - E\left(\left(\frac{1}{n}\sum t_i\right)^2\right)\\ &=\frac{1}{n}E\left(\sum t_i^2\right) - \frac{1}{n^2}E\left(\left(\sum t_i\right)^2\right)\\ &=\frac{1}{n}E\left(\sum t_i^2\right) - \frac{1}{n^2}E\left(\sum t_i^2+2\sum \sum_{j<i} t_it_j\right)\\ &=\frac{n-1}{n^2}\sum E\left(t_i^2\right) - \frac{2}{n^2}\sum \sum_{j<i}E\left( t_it_j\right) \end{align*} \]

那现在我们要推两个东西 一个是 \(E\left(t_i^2\right)\) 一个是 \(E\left( t_it_j\right)\)

(1) \(E\left(t_i^2\right)\)

跟刚才那个 \(E(t_i)\) 有什么区别

\[\begin{align*} E(t_i^2)&= p_i\cdot 1^2 + p_i(1-p_i)\cdot 2^2 + p_i(1-p_i)^2\cdot 3^2 +\dots\\ &= p_i(1^2+2^2(1-p_i)+3^2(1-p_i)^2+\dots)\\ \\ \because S&=1^2+2^2q+3^2q^2+\dots \\ \Rightarrow S&=\frac{1+3q+5q^2+7q^3+\dots}{1-q}\\ T&=1+3q+5q^2+7q^3+\dots \\ \Rightarrow T&=\frac{1+2q+2q^2+2q^3+\dots}{1-q}\\ &= \frac{1+q}{(1-q)^2}\\ \therefore S&=\frac{1+q}{(1-q)^3}\\ \therefore E(t_i^2)&=p_i\cdot\frac{2-p_i}{p_i^3}=\frac{2-p_i}{p_i^2} \end{align*} \]

(2) \(E\left( t_it_j\right)\)

直接考虑每种事件的概率啊!

\[\begin{align*} E(t_it_j)&=p_i\cdot E(t_j+1)+p_j\cdot E(t_i+1)+(1-p_i-p_j)\cdot E((t_i+1)(t_j+1))\\ &=p_i\cdot E(t_j+1)+p_j\cdot E(t_i+1)+(1-p_i-p_j)\cdot (E(t_it_j)+E(t_i)+E(t_j)+1)\\ &=1+(1-p_j)\cdot E(t_j)+(1-p_i)\cdot E(t_i)+(1-p_i-p_j)E(t_it_j)\\ \therefore (p_i+p_j)\cdot E(t_it_j)&=1+(1-p_j)\cdot E(t_j)+(1-p_i)\cdot E(t_i)\\ E(t_it_j)&=\frac{1}{p_i+p_j}+\frac{1-p_j}{p_i+p_j}\cdot E(t_j)+\frac{1-p_i}{p_i+p_j}\cdot E(t_i)\\ &=\frac{1}{p_i+p_j}+\frac{1-p_j}{p_j(p_i+p_j)}+\frac{1-p_i}{p_i(p_i+p_j)}\\ &=\frac{1}{p_i+p_j}+\frac{p_i+p_j-2p_ip_j}{p_ip_j(p_i+p_j)}\\ &=\frac{1}{p_ip_j}-\frac{1}{p_i+p_j} \end{align*} \]

诶诶然后就完了呢!\(\sim\) 代回去!

\[\begin{align*} E(\sigma^2) &=\frac{n-1}{n^2}\sum E\left(t_i^2\right) - \frac{2}{n^2}\sum \sum_{j<i}E\left( t_it_j\right) \\ &=\frac{n-1}{n^2}\sum \frac{2-p_i}{p_i^2}-\frac{2}{n^2}\sum \sum_{j<i}\left(\frac{1}{p_ip_j}-\frac{1}{p_i+p_j}\right) \end{align*} \]

这就完了. 直接做是 \(O(n^2\log n)\) 的,如果线性求 \((p_i+p_j)\) 逆元就可以 \(O(n^2)\) 了!

代码

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 5005, P = 998244353;
int n,n_inv,q[N],p[N],pi[N];
int ksm(int a, int b)
{
	int res=1;
	for( ; b; b>>=1, a=(ll)a*a%P) if(b&1) res=(ll)a*res%P;
	return res;
}
void init()
{
	scanf("%d",&n); int s=0; n_inv=ksm(n,P-2);
	for(int i=1; i<=n; i++) scanf("%d",&q[i]),s=(s+q[i])%P;
	s=ksm(s,P-2); for(int i=1; i<=n; i++) p[i]=(ll)q[i]*s%P;
	for(int i=1; i<=n; i++) pi[i]=ksm(p[i],P-2);
}
void work1()
{
	int res=0;
	for(int i=1; i<=n; i++) res=(res+pi[i])%P;
	printf("%lld\n",(ll)res*n_inv%P);
}
void work2()
{
	int res1=0,res2=0;
	for(int i=1; i<=n; i++) res1=(res1+(ll)(2-p[i]+P)*pi[i]%P*pi[i]%P)%P;
	res1=(ll)res1*(n-1+P)%P*n_inv%P*n_inv%P;
	for(int i=1; i<=n; i++) for(int j=1; j<i; j++)
		res2=(res2+(ll)pi[i]*pi[j]%P-ksm(p[i]+p[j],P-2)+P)%P;
	res2=(ll)res2*2*n_inv%P*n_inv%P;
	printf("%d\n",(res1-res2+P)%P);
	
}
int main()
{
	init(); work1(); work2(); return 0;
}

D 玩游戏

题目链接

DTOJ P6366

题面大意

集合 \(S:s_1, s_2, \dots , s_m\) ,队列 \(\{b_i\}:b_1, b_2, \dots , b_k\),D 和 A 交替进行以下操作,直到队列 \(b\) 为空:

  • \(b\) 中的第一个数放入 ,取走 \(S\) 中的一个数 \(x\),然后把它累积到自己的得分中。

\(\{a_i\}: a_1, a_2, \dots , a_n\)

进行 \(q\) 场游戏。每场游戏三个数 \(l,r,x\) 询问,若选用区间 \(a_l , a_{l+1}, \dots , a_r\) 作为 \(b\) 进行一场游戏,在二者都采用最优策略的情况下,位置 \(x\) 的元素 \(a_x\) 是被谁取走的。

对于所有测试数据,保证 \(1 \le n, m, q \le 10^6\)\(1 \le a_i , s_i \le n + m\)\(1 \le l \le x \le r \le n\),保证将 \(a, s\) 拼接后得到的序列为 \(1 \sim n + m\) 的一个排列。

题解

暴力

图:

我们可以通过观察和手模样例得出一个结论:

放入 \([l,t]\) 的数之后,如果取走了 \(x\) 那么 \(x\) 一定是 \(a[l\sim t]\cup S\) 中前 \(t-l+1\) 大的元素.

这个很好理解,假设 \(x\) 不是前 \(t-l+1\) 大,因为取走 \(x\) 之前只取了 \(t-l\) 个元素,所以一定有比 \(x\) 更大的数可以取.

所以我们就有一个 \(O(n\log^2n)\) 做法,甚至可以拿到 92 分的高分()

注意到第 \(t-l+1\) 大的元素就是第 \(m+1\) 小的元素,这是个常数,所以是单调递减的

首先我们在 \([x,r]\) 区间二分找 \(t\),使第 \(m+1\) 小的元素 \(= a_x\) 这个时候就是 \(a_x\) 被取走的时候

求区间第 \(k\) 小使用主席树

代码

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e6+5;
int n,m,q;
int a[N],s[N];
int rd()
{
	int x; char ch;
	while(!isdigit(ch=getchar()));
	for(x=(ch^48); isdigit(ch=getchar()); x=(x<<1)+(x<<3)+(ch^48));
	return x;
}
struct Seg
{
	int ls,rs,dat;
} t[N<<5];
int rt[N],rt2[N],tot;
void build(int &p, int l, int r, int x, int v)
{
	if(!p) p=++tot;
	if(l==r) { t[p].dat+=v; return ; }
	int mid=(l+r)>>1;
	if(x<=mid) build(t[p].ls,l,mid,x,v);
	else build(t[p].rs,mid+1,r,x,v);
	t[p].dat=t[t[p].ls].dat+t[t[p].rs].dat;
}
void change(int &p, int q, int l, int r, int x, int v)
{
	t[p=++tot]=t[q];
	if(l==r) { t[p].dat+=v; return ; }
	int mid=(l+r)>>1;
	if(x<=mid) change(t[p].ls,t[q].ls,l,mid,x,v);
	else change(t[p].rs,t[q].rs,mid+1,r,x,v);
	t[p].dat=t[t[p].ls].dat+t[t[p].rs].dat;
}
int query(int p, int q, int l, int r, int k)
{
	if(l==r) return l;
	int mid=(l+r)>>1;
	int lcnt=t[t[p].ls].dat-t[t[q].ls].dat; 
	if(k<=lcnt) return query(t[p].ls,t[q].ls,l,mid,k);
	else return query(t[p].rs,t[q].rs,mid+1,r,k-lcnt);
}
int main()
{
	n=rd(),m=rd(),q=rd();
	for(int i=1; i<=n; i++) a[i]=rd();
	for(int i=1; i<=m; i++) s[i]=rd();
	for(int i=1; i<=m; i++) build(rt[0],1,n+m,s[i],1);
	for(int i=1; i<=n; i++) change(rt[i],rt[i-1],1,n+m,a[i],1),change(rt2[i],rt2[i-1],1,n+m,a[i],1);
	int L,R,x;
	/*while(q--)
	{
		L=rd(),R=rd(),x=rd();
		printf("%d\n",query(rt[R],rt2[L-1],1,n+m,x));
	}*/
	while(q--)
	{	
		L=rd(),R=rd(),x=rd();
		if(query(rt[R],rt2[L-1],1,n+m,m+1)>a[x]) { puts("-1"); continue ; }
		int l=x,r=R;
		while(l<r)
		{ 
//			printf("%d %d\n",l,r);
			int mid=(l+r)>>1;
			if(query(rt[mid],rt2[L-1],1,n+m,m+1)<=a[x]) r=mid;
			else l=mid+1;
		}
//		printf("%d\n",l);
		puts(((l-L+1)&1)?"Diana":"Ava");
	}
	return 0;
}

正解

别催了别催了在补题了

补完了!

注意没有强制在线,我们离线做!

从小到大处理询问,先把集合 \(S\) 排个序,然后就可以用一个指针维护 \(S\) 中比 \(a[x]\) 小的数的个数.

然后以下标建线段树,就可以在线段树 \([l,r]\) 区间二分一个位置,使得 \(a[x]\) 是第 \(m+1\) 小.

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

线段树上二分确实是好东西

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e6+5;
int rd()
{
	int x; char ch;
	while(!isdigit(ch=getchar()));
	for(x=(ch^48); isdigit(ch=getchar()); x=(x<<1)+(x<<3)+(ch^48));
	return x;
}
struct Sagiri
{
	int l,r,dat;
} t[N<<2];
#define ls (p<<1)
#define rs (p<<1)|1
void build(int p, int l, int r)
{
	t[p].l=l,t[p].r=r;
	if(l==r) return ;
	int mid=(l+r)>>1;
	build(ls,l,mid),build(rs,mid+1,r);
	t[p].dat=t[ls].dat+t[rs].dat;	
}
void change(int p, int x, int v)
{
	if(t[p].l==t[p].r) { t[p].dat+=v; return ; }
	int mid=(t[p].l+t[p].r)>>1;
	if(x<=mid) change(ls,x,v);
	else if(x>mid) change(rs,x,v);
	t[p].dat=t[ls].dat+t[rs].dat;
}
int query(int p, int l, int r)
{
	if(t[p].l==l and t[p].r==r) return t[p].dat;
	int mid=(t[p].l+t[p].r)>>1;
	if(r<=mid) return query(ls,l,r);
	else if(l>mid) return query(rs,l,r);
	else return query(ls,l,mid)+query(rs,mid+1,r);
}
int query2(int p, int k)
{
	if(t[p].l==t[p].r) return t[p].l;
	if(t[ls].dat>=k) return query2(ls,k); // 线段树上二分
	else return query2(rs,k-t[ls].dat);
}
int n,m,q,nr;
int a[N],s[N];
struct Nazuna { int l,x,r,id,ans,val; } rq[N<<1]; // 拿小荠来离线询问!
void work()
{
	build(1,1,n);
	int cur=0;
	for(int i=1; i<=nr; i++)
	{
		while(cur<m and s[cur+1]<rq[i].val) cur++; 
		if(!rq[i].id) change(1,rq[i].x,1);
		else
		{
			if(query(1,rq[i].l,rq[i].r)<m-cur) rq[i].ans=n+1; 
			else
			{
				int t=m-cur+((rq[i].l>1)?query(1,1,rq[i].l-1):0); 
				if(t==0) rq[i].ans=rq[i].x; //记得各种特判
				else
				{
					int pos=query2(1,t);
					rq[i].ans=max(pos,rq[i].x);
				}
			}
		}
	}
}
int main()
{
	scanf("%d%d%d",&n,&m,&q);
	for(int i=1; i<=n; i++) 
	{
		scanf("%d",&a[i]);
		nr++; rq[nr].x=i,rq[nr].val=a[i];
	}
	for(int i=1; i<=m; i++) scanf("%d",&s[i]);
	sort(s+1,s+m+1);
	int l,r,x;
	for(int i=1; i<=q; i++)
	{
		scanf("%d%d%d",&l,&r,&x);
		nr++; rq[nr]=(Nazuna){l,x,r,i,0,a[x]};
	}
	sort(rq+1,rq+nr+1, [&] (const Nazuna &a, const Nazuna &b) { return a.val<b.val or (a.val==b.val and a.id>b.id); });
	work();
	sort(rq+1,rq+nr+1, [&] (const Nazuna &a, const Nazuna &b) { return a.id<b.id; });
	for(int i=1; i<=nr; i++) 
		if(rq[i].id)
		{
			if(rq[i].ans>rq[i].r) puts("-1");
			else puts(((rq[i].ans-rq[i].l+1)&1)?"Diana":"Ava");
		}
	return 0;
}

posted @ 2022-11-14 23:09  copper_carbonate  阅读(79)  评论(0)    收藏  举报