CF1917

好场。

A

0 0 0 结果一定为 0 0 0,直接输出 0 0 0 即可。

负数个数为偶数时,结果恒不为负,直接修改其中一个为 0 0 0 即可。

负数个数为奇数时,结果恒不为正,此时一定最优,不用修改。

B

对于一个字符串 s [ 1 ⋯ 5 ] s[1 \cdots 5] s[15],它的所有可能答案为

s 1 s [ 2 ⋯ 5 ] s_1s[2 \cdots 5] s1s[25]

s 1 s [ 3 ⋯ 5 ] , s 2 s [ 3 ⋯ 5 ] s_1s[3 \cdots 5],s_2s[3 \cdots 5] s1s[35],s2s[35]

s 1 s [ 4 ⋯ 5 ] , s 2 s [ 4 ⋯ 5 ] , s 3 s [ 4 ⋯ 5 ] s_1s[4 \cdots 5],s_2s[4 \cdots 5],s_3s[4 \cdots 5] s1s[45],s2s[45],s3s[45]

s 1 s 5 , s 2 s 5 , s 3 s 5 , s 4 s 5 s_1s_5,s_2s_5,s_3s_5,s_4s_5 s1s5,s2s5,s3s5,s4s5

s 1 , s 2 , s 3 , s 4 , s 5 s_1,s_2,s_3,s_4,s_5 s1,s2,s3,s4,s5

发现按长度分组后,每组的后缀相同,不同的只有第一个字符,开桶统计有多少种即可。

C

发现计数计的是 a i = i a_i=i ai=i 个数,而每次又是前缀加,则对于一个全 0 0 0 序列 a a a,每次计数最多使答案加 1 1 1

因此当序列全 0 0 0 后,我们进行一次加操作后进行一次计数,设序列全 0 0 0 后剩余操作次数为 p p p,则对答案贡献 ⌊ p 2 ⌋ \lfloor \frac{p}{2} \rfloor 2p

故我们只需考虑初始序列 a a a 在进行多少次加操作后计数,因为对答案的贡献 ≤ n \leq n n,那么加操作次数应 ≤ 2 n \leq 2n 2n,否则一定不如一次加操作一次计数优。

枚举加操作的次数统计即可,时间复杂度 O ( n 2 ) \text{O}(n^2) O(n2)

D

序列的构造方式引导我们将其分为 n n n 个长 k k k 的块,那么总答案就等于块内逆序对个数 + + + 块之间的逆序对个数。

块内的逆序对个数是好统计的,为排列 q q q 的逆序对个数 × n \times n ×n

考虑如何求块与块之间的逆序对个数,设当前的两块的编号为 i , j ( i < j ) i,j(i<j) i,j(i<j),若 p i < p j p_i<p_j pi<pj

2 t − 1 p i < p j < 2 t p i 2^{t-1} p_i<p_j<2^t p_i 2t1pi<pj<2tpi,则所有有关 p i , p j p_i,p_j pi,pj 的元素从小到大排列为

p i , ⋯   , 2 t − 1 p i , p j , 2 t p i , 2 p j , ⋯   , 2 k − 1 p i , 2 k − t p j , ⋯   , 2 k − 1 p j p_i,\cdots,2^{t-1} p_i,p_j,2^t p_i,2p_j,\cdots,2^{k-1} p_i,2^{k-t} p_j,\cdots,2^{k-1} p_j pi,,2t1pi,pj,2tpi,2pj,,2k1pi,2ktpj,,2k1pj

对于每个 2 m p i 2^m p_i 2mpi,它对答案的贡献为比它小的所有有关 p j p_j pj 的元素个数,那么总和为

1 + ⋯ + ( k − t ) = ( k − t + 1 ) ( k − t ) 2 1+\cdots+(k-t)=\frac{(k-t+1)(k-t)}{2} 1++(kt)=2(kt+1)(kt)

p i > p j p_i>p_j pi>pj

2 t − 1 p i < p j < 2 t p i 2^{t-1} p_i<p_j<2^t p_i 2t1pi<pj<2tpi,则所有有关 p i , p j p_i,p_j pi,pj 的元素从小到大排列为

p j , ⋯   , 2 t − 1 p j , p i , 2 t p j , 2 p i , ⋯   , 2 k − 1 p j , 2 k − t p i , ⋯   , 2 k − 1 p i p_j,\cdots,2^{t-1} p_j,p_i,2^t p_j,2p_i,\cdots,2^{k-1} p_j,2^{k-t} p_i,\cdots,2^{k-1} p_i pj,,2t1pj,pi,2tpj,2pi,,2k1pj,2ktpi,,2k1pi

总和为

t + ⋯ + ( k − 1 ) + t k = ( t + k − 1 ) ( k − t ) 2 + t k t+\cdots+(k-1)+tk=\frac{(t+k-1)(k-t)}{2}+tk t++(k1)+tk=2(t+k1)(kt)+tk

暴力统计块与块之间的答案是 O ( n 2 log ⁡ n ) \text{O}(n^2 \log n) O(n2logn) 的,发现计算式只与 t , k t,k t,k 有关,则对于所有满足 2 t − 1 p i < p x < 2 t p i 2^{t-1} p_i<p_x<2^t p_i 2t1pi<px<2tpi p x p_x px,它们的计算方式都是一样的,于是考虑树状数组统计 [ 2 t − 1 p i , 2 t p i ) [2^{t-1}p_i,2^t p_i) [2t1pi,2tpi) 的数的个数,时间复杂度 O ( n log ⁡ 2 n ) \text{O}(n \log^2 n) O(nlog2n)

#include<cstdio>
#include<algorithm>
using namespace std;
const int N=4e5+5;
const int mod=998244353;
int n,k,mx,a[N],b[N],c[N];
int read()
{
	int res,f=1;
	char ch;
	while((ch=getchar())<'0'||ch>'9')
	if(ch=='-')
	f=-1;
	res=ch^48;
	while((ch=getchar())>='0'&&ch<='9')
	res=(res<<1)+(res<<3)+(ch^48);
	return res*f;
}
int lowbit(int x)
{
	return x&(-x);
}
void add(int x,int v)
{
	for(;x<=mx;x+=lowbit(x))
	c[x]+=v;
}
int ask(int x)
{
	int ans=0;
	for(x=min(x,mx);x;x-=lowbit(x))
	ans+=c[x];
	return ans; 
}
int cal(int x)
{
	return 1ll*x*(x+1)/2%mod;
}
void solve()
{
	int i,j,num,ans=0,x,tmp;
	n=read();k=read();mx=max(n*2-1,k);
	for(i=1;i<=n;i++)
	a[i]=read();
	for(i=1;i<=k;i++)
	{
		b[i]=read()+1;
		add(b[i],1);
	}
	for(i=1;i<=k;i++)
	{
		add(b[i],-1);
		ans=(ans+ask(b[i]))%mod;
	}
	ans=1ll*ans*n%mod;
	for(i=1;i<=n;i++)
	add(a[i],1);
	for(i=1;i<=n;i++)
	{
		add(a[i],-1);
		for(j=1,tmp=a[i];tmp<=2*n-1;j++,tmp*=2)
		{
			num=ask(tmp*2)-ask(tmp);
			x=max(0,k-j);
			ans=(ans+1ll*cal(x)*num%mod)%mod;
		}
		for(j=1,tmp=a[i];tmp>1;j++,tmp=(tmp+1)/2)
		{
			num=ask(tmp-1)-ask((tmp+1)/2-1);
			x=min(j,k);
			ans=(ans+1ll*num*
			(1ll*(k-1+x)*(k-x)/2%mod+1ll*k*x%mod)%mod)%mod;
		}
	}
	printf("%d\n",ans);
}
int main()
{
	int T=read();
	while(T--)
	solve();
	return 0;
}

E

显然 k k k 为奇数无解。

4 ∣ k 4|k 4∣k,暴力填满所有能填的 2 × 2 2 \times 2 2×2 方格即可。

否则 2 ∣ k 2|k 2∣k 4 ∤ k 4 \nmid k 4k,先特判掉 n = 2 , k = 2 n=2,k=2 n=2,k=2 的合法情况。

发现 k = 2 k=2 k=2 k = n 2 − 2 k=n^2-2 k=n22 时无解。

先空出左上 4 × 4 4 \times 4 4×4 方阵,在保证填完这轮后剩余能填的方格 ≥ 6 \geq 6 6 的基础上,将剩余能填的 2 × 2 2 \times 2 2×2 方格填满。

此时剩余的 k k k 只会是 6 6 6 10 10 10,填出合法方案即可。

#include<cstdio>
using namespace std;
const int N=1e3+5;
int n,k;
bool bz[N][N];
int read()
{
	int res,f=1;
	char ch;
	while((ch=getchar())<'0'||ch>'9')
	if(ch=='-')
	f=-1;
	res=ch^48;
	while((ch=getchar())>='0'&&ch<='9')
	res=(res<<1)+(res<<3)+(ch^48);
	return res*f;
}
void solve()
{
	int i,j;
	n=read();k=read();
	if(k&1)
	{
		printf("No\n");
		return;
	}
	for(i=1;i<=n;i++)
		for(j=1;j<=n;j++)
		bz[i][j]=0;
	if(k%4==0)
	{
		printf("Yes\n");
		for(i=1;i<=n&&k;i+=2)
			for(j=1;j<=n&&k;j+=2)
			{
				bz[i][j]=bz[i][j+1]=bz[i+1][j]=bz[i+1][j+1]=1;
				k-=4;
			}
		for(i=1;i<=n;i++)
		{
			for(j=1;j<=n;j++)
			printf("%d ",bz[i][j]);
			printf("\n");
		}
		return;
	}
	if(n==2&&k==2)
	{
		printf("Yes\n");
		printf("1 0\n0 1\n");
		return;
	}
	if(k==2||k==n*n-2)
	{
		printf("No\n");
		return;
	}
	printf("Yes\n");
	for(i=1;i<=n&&k>6;i+=2)
		for(j=1;j<=n&&k>6;j+=2)
		if(i>=5||j>=5)
		{
			bz[i][j]=bz[i][j+1]=bz[i+1][j]=bz[i+1][j+1]=1;
			k-=4;
		}
	if(k==6)
	bz[1][1]=bz[1][2]=bz[2][1]=bz[2][3]=bz[3][2]=bz[3][3]=1;
	else
	{
		if(k==10)
		{
			for(i=1;i<=4;i++)
				for(j=1;j<=4;j++)
				bz[i][j]=1;
			bz[1][1]=bz[1][2]=bz[2][1]=bz[2][3]=bz[3][2]=bz[3][3]=0;
		}
	}
	for(i=1;i<=n;i++)
	{
		for(j=1;j<=n;j++)
		printf("%d ",bz[i][j]);
		printf("\n");
	}
}
int main()
{
	int T=read();
	while(T--)
	solve();
	return 0;
}

F

把边按边权从小到大排序后,显然,合法的 d d d 应该 ≥ l n + l n − 1 \geq l_n+l_{n-1} ln+ln1

考虑把树建成一条链加一个菊花图的形式(谔,应该叫毛毛虫?),如果存在用前 n − 1 n-1 n1 条边凑出长度为 d − l n d-l_n dln 的链的合法方案,则有解,跑背包检验即可,时间复杂度 O ( n d ) \text{O}(nd) O(nd)

否则进一步的,令链上边集为 e 1 e_1 e1,与 e 1 e_1 e1 不交的一个边集 e 2 e_2 e2,满足 ∑ i ∈ e 1 l i + ∑ i ∈ e 2 l i = d \sum_{i \in e_1}l_i+\sum_{i \in e_2}l_i=d ie1li+ie2li=d,且 ∑ i ∈ e 1 l i ≥ l n , ∑ i ∈ e 2 l i ≥ l n \sum_{i \in e_1}l_i \geq l_n,\sum_{i \in e_2}l_i \geq l_n ie1liln,ie2liln,存在即有解。

f i , j f_{i,j} fi,j 表示 ∑ k ∈ e 1 l k = i , ∑ k ∈ e 2 l k = j \sum_{k \in e_1}l_k=i,\sum_{k \in e_2}l_k=j ke1lk=i,ke2lk=j 的方案是否存在,时间复杂度 O ( n d 2 ) \text{O}(nd^2) O(nd2),用 bitset \text{bitset} bitset 优化,可以通过。

#include<cstdio>
#include<bitset>
#include<algorithm>
using namespace std;
const int N=2e3+5;
int n,d,a[N];
bitset<N> bz[N];
int read()
{
	int res,f=1;
	char ch;
	while((ch=getchar())<'0'||ch>'9')
	if(ch=='-')
	f=-1;
	res=ch^48;
	while((ch=getchar())>='0'&&ch<='9')
	res=(res<<1)+(res<<3)+(ch^48);
	return res*f;
}
void solve()
{
	int i,j;
	bool tag;
	n=read();d=read();
	for(i=1;i<=n;i++)
	a[i]=read();
	sort(a+1,a+n+1);
	if(a[n]+a[n-1]>d)
	{
		printf("No\n");
		return;
	}
	for(i=0;i<=d;i++)
	bz[i].reset();
	bz[0][0]=1;
	for(i=1;i<=n-1;i++)
		for(j=d;j>=0;j--)
		{
			if(j+a[i]<=d)
			bz[j+a[i]]|=bz[j];
			bz[j]|=bz[j]<<a[i];
		}
	tag=bz[0][d-a[n]];
	for(i=a[n];i<=d-a[n];i++)
	tag|=bz[i][d-i];
	printf(tag?"Yes\n":"No\n");
}
int main()
{
	int T=read();
	while(T--)
	solve();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值