视频讲解:BV1vN4y1M7cx
给定整数 n ( 1 ≤ n ≤ 1 0 9 ) n(1\leq n \leq 10^9) n(1≤n≤109) ,求任意一组 a , b , c ( 0 ≤ a , b , c ≤ 1 0 9 ) a,b,c(0 \leq a,b,c \leq 10^9) a,b,c(0≤a,b,c≤109) ,满足
( a ⊕ b ) + ( b ⊕ c ) + ( a ⊕ c ) = n (a \oplus b)+(b \oplus c)+(a \oplus c)=n (a⊕b)+(b⊕c)+(a⊕c)=n
无解输出 − 1 -1 −1 。
仅考虑最低位的情况,会发现:
因此对于任意一位,要不总和为 0 0 0 ,要不向更高位进一位。
因此若 n n n 为奇数,则无解。
有解时,一种合法的方案为 ( n 2 , 0 , 0 ) (\frac{n}{2},0,0) (2n,0,0) 。
#include
using namespace std;
typedef long long ll;
int main()
{
int T,n;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
if(n&1)
printf("-1\n");
else
printf("%d %d %d\n",n/2,0,0);
}
}
给定两个偶数 n , m ( 2 ≤ n , m ≤ 50 ) n,m(2\leq n,m \leq 50) n,m(2≤n,m≤50) ,求 n × m n \times m n×m 的01矩阵,满足对于矩阵内任意位置 ( i , j ) (i,j) (i,j) ,在四邻域上均有恰好2个与该元素不同的元素。
用以下矩阵及其旋转90°的矩阵填充即可:
01
10
#include
using namespace std;
typedef long long ll;
const int MAXN=55;
int a[MAXN][MAXN];
int main()
{
int T,n,m,i,j;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
memset(a,0,sizeof(a));
for(i=0;i<n;i+=2)
{
for(j=0;j<m;j+=2)
{
if((i/2+j/2)%2)
a[i][j]=a[i+1][j+1]=1;
else
a[i+1][j]=a[i][j+1]=1;
}
}
for(i=0;i<n;i++)
{
for(j=0;j<m;j++)
printf("%d ",a[i][j]);
puts("");
}
}
}
给定长度为 n n n 的 1 1 1 到 n − 1 n-1 n−1 的排列 a a a ,求有多少排列 b b b 与排列 a a a “相似”。
“相似”的定义为,对于任意区间 [ l , r ] ( 1 ≤ l ≤ r ≤ n ) [l,r](1 \leq l \leq r \leq n) [l,r](1≤l≤r≤n) ,均有
M E X ( [ a l , a l + 1 , . . . , a r ] ) = M E X ( [ b l , b l + 1 , . . . , b r ] ) MEX([a_l,a_{l+1},...,a_r])=MEX([b_l,b_{l+1},...,b_r]) MEX([al,al+1,...,ar])=MEX([bl,bl+1,...,br])
根据 M E X MEX MEX 的定义,我们需要从小到大逐个考虑每个数。
设 p o s i pos_i posi 表示排列 a a a 中元素 i i i 出现的位置。
考虑用类似数学归纳法的思想,求解排列 b b b 的方案数。
初始区间 [ p o s 0 , p o s 0 ] [pos_0,pos_0] [pos0,pos0] 的 M E X MEX MEX 值必须为 1 1 1 ,因此 0 0 0 位置确定,方案数为 1 1 1 。
设当前区间为 [ l , r ] [l,r] [l,r] , 0 0 0 到 i − 1 i-1 i−1 的元素均在改区间中。
对于元素 i i i :
#include
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int MAXN=100100;
int pos[MAXN];
int main()
{
int T,n,x,i,l,r;
ll ans;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(i=1;i<=n;i++)
{
scanf("%d",&x);
pos[x]=i;
}
l=r=pos[0];
ans=1;
for(i=1;i<n;i++)
{
if(l<=pos[i]&&pos[i]<=r)
ans=ans*(r-l+1-i)%mod;
else
{
l=min(l,pos[i]);
r=max(r,pos[i]);
}
}
printf("%lld\n",ans);
}
}
给定长度为 n ( 1 ≤ n ≤ 5000 ) n(1 \leq n \leq 5000) n(1≤n≤5000) 的数组 a a a 。进行若干次操作,每次操作可以选择一个 i ( 1 ≤ i < n ) i(1\leq i
以任意方式进行若干次操作,求最后剩下完全相同的元素时,数组最长可以是多少。
考虑对于任意区间 [ l , r ] [l,r] [l,r] ,什么情况下能将其中的元素恰好完全删除。
满足以下两个条件时,方可:
通过预处理或枚举区间端点等方式,可以很快判断任意区间能否完全删除。
设 d p i dp_i dpi 表示在前 i i i 个元素中,最后剩下 a i a_i ai 时,最多可以剩下多时少元素。若无法剩下 a i a_i ai 则为负无穷大。初始 d p 0 = 0 dp_0=0 dp0=0 。
则有转移式:
d p i = max j = 0 i − 1 ( D e l e t a b l e ( j + 1 , r ) & ( a i = a j ∣ j = 0 ) ) ∗ ( d p j + 1 ) dp_i=\max_{j=0}^{i-1}(Deletable(j+1,r)\&(a_i=a_j|j=0))*(dp_j+1) dpi=j=0maxi−1(Deletable(j+1,r)&(ai=aj∣j=0))∗(dpj+1)
两重循环枚举 i , j i,j i,j 即可。
枚举 j j j 时倒序枚举以便求解 D e l e t a b l e ( j + 1 , r ) Deletable(j+1,r) Deletable(j+1,r) 。
最终答案为
a n s = max i = 1 n D e l e t a b l e ( i + 1 , n ) ∗ d p i ans=\max_{i=1}^{n} Deletable(i+1,n)*dp_i ans=i=1maxnDeletable(i+1,n)∗dpi
#include
using namespace std;
typedef long long ll;
const int MAXN=5050;
const int inf=1<<30;
int a[MAXN],sum[MAXN],dp[MAXN];
int main()
{
int T,n,i,j,mxnum,ans;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(i=1;i<=n;i++)
scanf("%d",&a[i]);
for(i=1;i<=n;i++)
{
memset(sum,0,sizeof(int)*(n+5));
dp[i]=-inf;
mxnum=0;
for(j=i-1;j>=0;j--)
{
if((i-j-1)%2==0&&mxnum<=(i-j-1)/2&&(j==0||a[i]==a[j]))
dp[i]=max(dp[i],dp[j]+1);
sum[a[j]]++;
mxnum=max(mxnum,sum[a[j]]);
}
}
memset(sum,0,sizeof(int)*(n+5));
ans=0;
mxnum=0;
for(i=n;i>=1;i--)
{
if((n-i)%2==0&&mxnum<=(n-i)/2)
ans=max(ans,dp[i]);
sum[a[i]]++;
mxnum=max(mxnum,sum[a[i]]);
}
printf("%d\n",ans);
}
}
给定大小为 n ( 1 ≤ n ≤ 1 0 6 ) n(1 \leq n \leq 10^6) n(1≤n≤106) 的多重集合 A A A ,每个元素在 [ 1 , m ] ( 1 ≤ m ≤ 5 ⋅ 1 0 6 ) [1,m](1 \leq m \leq 5 \cdot 10^6) [1,m](1≤m≤5⋅106) 范围内。
可以进行任意次操作。每次操作选择 A A A 中一个元素 x x x ,将其拆分为 x = p ⋅ q x=p\cdot q x=p⋅q ,其中 p , q > 1 p,q >1 p,q>1 ,然后将 p , q p,q p,q 插入 A A A 中,并删除 x x x 。
求 max ( a i ) − min ( a i ) \max(a_i)-\min(a_i) max(ai)−min(ai) 的最小值。
参考了huangxiaohua的AC代码和官方题解。
考虑枚举 L = m i n ( a i ) L=min(a_i) L=min(ai) ,求解最小的 R = m a x ( a i ) R=max(a_i) R=max(ai) 。
从大到小枚举 L L L ,当枚举到新的 L L L 时,会发现对于 L L L 的倍数 j j j ,可能有更好的拆分方法,从而得到更小的 R R R 。对于不是 L L L 的倍数的数,并不会受到影响。
设 d p i , j dp_{i,j} dpi,j 表示将 j j j 拆分为若干因子后,最小因子为 i i i 时所有因子的最大值的最小值,其中 $j\geq i $ 且 i ∣ j i|j i∣j 。
对于 k ≥ i k\geq i k≥i , 若 d p k , j i ≥ i dp_{k,\frac{j}{i}}\geq i dpk,ij≥i ,则可以将 j j j 拆出一个 i i i ,变成子状态 d p k , j i dp_{k,\frac{j}{i}} dpk,ij ,即
d p i , j = min ( d p i , j , d p k , j i ) dp_{i,j}=\min(dp_{i,j},dp_{k,\frac{j}{i}}) dpi,j=min(dpi,j,dpk,ij)
实现时, i i i 维度可以通过从大到小枚举被压缩掉,复杂度 O ( m log m ) O(m\log m) O(mlogm)。
当枚举到新的 L L L 时,对于初始就有的元素 j j j ,其中 L ∣ j L|j L∣j ,先删去所有旧的 d p k , j dp_{k,j} dpk,j ,其中 k > L k>L k>L ,再更新并添加上新的 d p L , j dp_{L,j} dpL,j 。
#include
using namespace std;
typedef long long ll;
const int MAXM=5000500;
int has[MAXM],num[MAXM],dp[MAXM];
int main()
{
int T,n,m,l,r,ans,i,j,x;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
memset(has,0,sizeof(int)*(m+5));
memset(num,0,sizeof(int)*(m+5));
for(i=1;i<=m;i++)
dp[i]=i;
l=MAXM;r=-1;
while(n--)
{
scanf("%d",&x);
has[x]=num[x]=1;
l=min(l,x);
r=max(r,x);
}
ans=r-l;
for(i=sqrt(m)+1;i>=1;i--)
{
for(j=i;j<=m;j+=i)
{
if(has[j])
num[dp[j]]--;
if(dp[j/i]>=i)
dp[j]=min(dp[j/i],dp[j]);
if(has[j])
num[dp[j]]++;
}
while(num[r]==0)
r--;
ans=min(ans,r-min(i,l));
}
printf("%d\n",ans);
}
}