视频讲解:TBD
A. Domino Disaster
题目大意
有一个 2 × n ( 1 ≤ n ≤ 100 ) 2\times n~(1 \leq n \leq 100) 2×n (1≤n≤100) 的方格图,用 1 × 2 1\times 2 1×2 的多米诺骨牌填满,每个骨牌是垂直或者水平放置的,且每个格子最多被一个骨牌覆盖。
现在给定其中一行的网格,求另一行是什么样子。
题解
‘L’ 和 ‘R’ 不变, ‘D’ 和 ‘U’ 互换。
参考代码
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
int main()
{
int T,n,i;
string s;
cin>>T;
while(T--)
{
cin>>n;
cin>>s;
for(i=0;i<n;i++)
{
if(s[i]=='U')
s[i]='D';
else if(s[i]=='D')
s[i]='U';
}
cout<<s<<endl;
}
}
B. MEXor Mixup
题目大意
给定两个整数 a , b ( a > 0 , b ≥ 0 ) a,b~(a>0,b \geq 0) a,b (a>0,b≥0) ,求最小的非负整数数组大小,使得数组的 M E X MEX MEX 值为 a a a , X O R XOR XOR 值为 b b b 。
题解
设 0 0 0 到 i i i 的异或和为 p r e i pre_i prei 。有以下几种情况:
- 若 p r e a − 1 = b pre_{a-1}=b prea−1=b ,则数组为 0 0 0 到 a − 1 a-1 a−1 即可,答案为 a a a ;
- 若 p r e a − 1 ≠ b , p r e a − 1 ⊕ a = b pre_{a-1}\neq b,pre_{a-1}\oplus a=b prea−1=b,prea−1⊕a=b ,则数组为 0 0 0 到 a − 1 a-1 a−1 ,在加任意两个大于 a a a 的元素 x , y x,y x,y,满足 x ⊕ y = a ⊕ b x\oplus y=a\oplus b x⊕y=a⊕b 即可,答案为 a + 2 a+2 a+2 ;
- 若 p r e a − 1 ≠ b , p r e a − 1 ⊕ a ≠ b pre_{a-1}\neq b,pre_{a-1}\oplus a \neq b prea−1=b,prea−1⊕a=b ,则数组为 0 0 0 到 a − 1 a-1 a−1 ,在加一个元素 a ⊕ b a\oplus b a⊕b 即可,答案为 a + 1 a+1 a+1 ;
参考代码
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int MAXN=300300;
int pre[MAXN];
int main()
{
int T,a,b,i,c,ans;
for(i=1;i<MAXN;i++)
pre[i]=pre[i-1]^i;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&a,&b);
c=pre[a-1]^b;
if(c==0)
ans=a;
else if(c==a)
ans=a+2;
else
ans=a+1;
printf("%d\n",ans);
}
}
C. Carrying Conundrum
题目大意
有人计算十进制加法时,进位错了。原本若当前位大于 10 10 10 则进位到左边的第一位,结果算错成进位到左边的第二位。
给定 n ( 1 ≤ n ≤ 1 0 9 ) n~(1 \leq n \leq 10^9) n (1≤n≤109) ,求有多少对正整数对 ( a , b ) (a,b) (a,b) 满足其在错误进位的加法下,之和为 n n n 。
题解
会发现奇数数位和偶数数位的运算相互独立,因此直接拆分单独求结果即可。
设奇数数位提取后为
a
a
a ,偶数数位提取后为
b
b
b ,则非负整数对的答案为
(
a
+
1
)
⋅
(
b
+
1
)
(a+1)\cdot(b+1)
(a+1)⋅(b+1) ,再减去存在
0
0
0 的项,因此答案为
(
a
+
1
)
(
b
+
1
)
−
2
(a+1)(b+1)-2
(a+1)(b+1)−2 。
参考代码
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
int main()
{
ll T,n,a,b,i,j,ans;
scanf("%lld",&T);
while(T--)
{
scanf("%lld",&n);
a=b=0;
for(i=1,j=1;i<=n;i*=100,j*=10)
a+=n/i%10*j;
for(i=10,j=1;i<=n;i*=100,j*=10)
b+=n/i%10*j;
ans=(a+1)*(b+1)-2;
printf("%lld\n",ans);
}
}
D. Expression Evaluation Error
题目大意
有 n ( 1 ≤ n ≤ min ( 100 , s ) ) n(1 \leq n \leq \min(100,s)) n(1≤n≤min(100,s)) 个十进制正整数,其和在十进制下为 s ( 1 ≤ s ≤ 1 0 9 ) s(1 \leq s \leq 10^9) s(1≤s≤109) 。有人将这 n n n 个数看成11进制的数了,求这 n n n 个数为多少时,其按11进制求和会得到最大的结果。
题解
若在十进制下存在 1 0 k 10^k 10k ,则其在十一进制下的值为 1 1 k 11^k 11k 。若将其分配到 10 10 10 个数上,则之和为 10 × 1 1 k − 1 10\times 11^{k-1} 10×11k−1 ,显然变小了。
因此得到贪心策略:若 n = a 1 a 2 . . . a k ‾ n=\overline{a_1a_2...a_k} n=a1a2...ak ,则将其分解为 a 1 a_1 a1 个 1 ⋅ 1 0 k − 1 1\cdot 10^{k-1} 1⋅10k−1 , a 2 a_2 a2 个 1 ⋅ 1 0 k − 2 1\cdot 10^{k-2} 1⋅10k−2 ,…, a k a_k ak 个 1 1 1 最优。若数量超出 n n n 个,则将其以任意方式合并,答案不变。若数量不足 n n n 个,则优先将较小的数拆分为若干个 1 1 1 ,损失最小。
实现时,不妨先将每个数设为 1 1 1 ,然后逐个修改为 1 0 x 10^x 10x ,其中 x x x 尽可能大。若还有多余,则合并到任意元素上即可。
参考代码
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int MAX=1e9;
int main()
{
int T,s,n,i,j,a[110];
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&s,&n);
for(i=1;i<=n;i++)
a[i]=1;
s-=n;
for(i=MAX;i>=1;i/=10)
{
for(j=1;s>=i-1&&j<=n;j++)
{
if(a[j]>1)
continue;
a[j]+=i-1;
s-=i-1;
}
}
a[1]+=s;
for(i=1;i<=n;i++)
printf("%d%c",a[i],i==n?'\n':' ');
}
}
E. Non-Decreasing Dilemma
题目大意
有一个长度为 n ( 1 ≤ n ≤ 2 ⋅ 1 0 5 ) n~(1 \leq n \leq 2 \cdot 10^5) n (1≤n≤2⋅105) 的数组 a ( 1 ≤ a i ≤ 1 0 9 ) a~(1 \leq a_i \leq 10^9) a (1≤ai≤109) ,有以下两种形式的 q ( 1 ≤ q ≤ 2 ⋅ 1 0 5 ) q~(1 \leq q \leq 2 \cdot 10^5) q (1≤q≤2⋅105) 次询问:
- 1 x y 1\;x\;y 1xy - 将 a x ( 1 ≤ x ≤ n ) a_x (1 \leq x \leq n) ax(1≤x≤n) 修改为 y ( 1 ≤ y ≤ 1 0 9 ) y~(1 \leq y \leq 10^9) y (1≤y≤109) ;
- 2 l r 2\;l\;r 2lr - 求 [ a l , a l + 1 , . . . , a r ] [a_l,a_{l+1},...,a_r] [al,al+1,...,ar] 中,有多少个连续不下降区间,即有多少对 ( p , q ) (p,q) (p,q) 满足 l ≤ p ≤ q ≤ r l\leq p \leq q \leq r l≤p≤q≤r 且 a p ≤ a p + 1 ≤ . . . ≤ a q a_{p} \leq a_{p+1}\leq ... \leq a_q ap≤ap+1≤...≤aq ;
题解
区间合并线段树模板题。
参考代码
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
#define lson i<<1
#define rson i<<1|1
const int MAXN=200200;
int a[MAXN];
struct Seg
{
int l,r;
ll ans,llen,rlen;
}seg[MAXN<<2];
Seg merge(Seg s1,Seg s2)
{
Seg ret;
ret.ans=s1.ans+s2.ans;
ret.l=s1.l; ret.r=s2.r;
if(s1.llen==s1.r-s1.l+1&&a[s1.r]<=a[s2.l])
ret.llen=s1.llen+s2.llen;
else
ret.llen=s1.llen;
if(s2.r-s2.l+1==s2.rlen&&a[s1.r]<=a[s2.l])
ret.rlen=s1.rlen+s2.rlen;
else
ret.rlen=s2.rlen;
if(a[s1.r]<=a[s2.l])
ret.ans+=s1.rlen*s2.llen;
return ret;
}
void pushUp(int i)
{
seg[i]=merge(seg[lson],seg[rson]);
}
void build(int i,int l,int r)
{
if(l==r)
{
seg[i].l=seg[i].r=l;
seg[i].llen=seg[i].rlen=1;
seg[i].ans=1;
return;
}
int mid=(l+r)>>1;
build(lson,l,mid);
build(rson,mid+1,r);
pushUp(i);
}
void change(int i,int x)
{
if(seg[i].l==seg[i].r)
{
return;
}
int mid=(seg[i].l+seg[i].r)>>1;
if(x<=mid)
change(lson,x);
else
change(rson,x);
pushUp(i);
}
Seg query(int i,int l,int r)
{
if(seg[i].l==l&&seg[i].r==r)
{
return seg[i];
}
int mid=(seg[i].l+seg[i].r)>>1;
if(r<=mid)
return query(lson,l,r);
else if(l>=mid+1)
return query(rson,l,r);
else
return merge(query(lson,l,mid),query(rson,mid+1,r));
}
int main()
{
int n,q,i,op,x,y;
scanf("%d%d",&n,&q);
for(i=1;i<=n;i++)
scanf("%d",&a[i]);
build(1,1,n);
while(q--)
{
scanf("%d%d%d",&op,&x,&y);
if(op==1)
{
a[x]=y;
change(1,x);
}
else
{
printf("%lld\n",query(1,x,y).ans);
}
}
}
F. One-Four Overload
题目大意
有一个 n × m ( 1 ≤ n , m ≤ 500 ) n\times m~(1 \leq n,m \leq 500) n×m (1≤n,m≤500) 的方格图,其中有些格子被标记,不存在被标记的格子与网格边缘相邻(本题相邻均指四邻域)。
现在需要按照以下规则将每个格子填上数:
- 每个未标记的格子包含的数是 1 1 1 或 4 4 4 ;
- 每个被标记的格子包含的数是其相邻所有格子所包含的数之和;
- 每个被标记的格子包含的数是 5 5 5 的倍数;
求符合条件的这样的网格填数方案,若不存在则输出无解。
题解
为便于描述,称被标记的格子为X,未被标记的格子为Y。
易得X包含的数必定是
5
5
5 或
10
10
10 ,若X相邻的Y的数量为奇数,则不合法。
在合法的情况下,每个X必定在某一环上。即Y被X分割为若干连通块。
- 对于横向连续的X,保证其上下两个Y不同。
- 对于纵向连续的X,保证其左右两个Y不同。
- 对于拐角处的的X,保证其外侧两个Y不同。
- 对于单独的X,保证其有两个 1 1 1 和两个 4 4 4 。
综上,我们可以如下构造:
从上到下,从左到右逐个
1
,
4
1,4
1,4 交替填数,这样可以保证对于单独的X和拐角的X合法。
若碰到当前格子和上一格子均为X,则修改
1
,
4
1,4
1,4 交替的次序。这样,可以保证纵向连续的X合法,且由于横向连续的X上下的Y相互不连通且不存在奇数个Y相邻的X,因此也合法。
参考代码
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int MAXN=550;
int g[2][4]={{-1,0,0,1},{0,-1,1,0}};
char mz[MAXN][MAXN];
int ans[MAXN][MAXN];
int main()
{
int n,m,i,j,k,flag,cnt;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
scanf("%s",mz[i]+1);
flag=1;
for(i=1;i<=n&&flag;i++)
{
for(j=1;j<=m;j++)
{
if(mz[i][j]=='X')
{
cnt=0;
for(k=0;k<4;k++)
{
if(mz[i+g[0][k]][j+g[1][k]]=='.')
cnt++;
}
if(cnt&1)
{
flag=0;
break;
}
ans[i][j]=cnt/2*5;
}
}
}
if(!flag)
{
printf("NO\n");
}
else
{
printf("YES\n");
for(i=1,k=1;i<=n;i++,k=5-k)
{
for(j=1;j<=m;j++)
{
if(mz[i][j]=='.')
ans[i][j]=k;
else if(mz[i-1][j]=='X')
k=5-k;
}
}
for(i=1;i<=n;i++)
{
for(j=1;j<=m;j++)
printf("%d ",ans[i][j]);
puts("");
}
}
}