好场。
A
有 0 0 0 结果一定为 0 0 0,直接输出 0 0 0 即可。
负数个数为偶数时,结果恒不为负,直接修改其中一个为 0 0 0 即可。
负数个数为奇数时,结果恒不为正,此时一定最优,不用修改。
B
对于一个字符串 s [ 1 ⋯ 5 ] s[1 \cdots 5] s[1⋯5],它的所有可能答案为
s 1 s [ 2 ⋯ 5 ] s_1s[2 \cdots 5] s1s[2⋯5]
s 1 s [ 3 ⋯ 5 ] , s 2 s [ 3 ⋯ 5 ] s_1s[3 \cdots 5],s_2s[3 \cdots 5] s1s[3⋯5],s2s[3⋯5]
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[4⋯5],s2s[4⋯5],s3s[4⋯5]
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 2t−1pi<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,⋯,2t−1pi,pj,2tpi,2pj,⋯,2k−1pi,2k−tpj,⋯,2k−1pj
对于每个 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+⋯+(k−t)=2(k−t+1)(k−t)
若 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 2t−1pi<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,⋯,2t−1pj,pi,2tpj,2pi,⋯,2k−1pj,2k−tpi,⋯,2k−1pi
总和为
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+⋯+(k−1)+tk=2(t+k−1)(k−t)+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 2t−1pi<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) [2t−1pi,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 4∤k,先特判掉 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=n2−2 时无解。
先空出左上 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+ln−1。
考虑把树建成一条链加一个菊花图的形式(谔,应该叫毛毛虫?),如果存在用前 n − 1 n-1 n−1 条边凑出长度为 d − l n d-l_n d−ln 的链的合法方案,则有解,跑背包检验即可,时间复杂度 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 ∑i∈e1li+∑i∈e2li=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 ∑i∈e1li≥ln,∑i∈e2li≥ln,存在即有解。
令 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 ∑k∈e1lk=i,∑k∈e2lk=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;
}