A.水题
很明显对于某一列如果已知行为 D D D,那么填 U U U。
如果已知行为 U U U,那么填 D D D
否则要不填 L L L,要不填 R R R
一定是 L R L R … LRLR\dots LRLR…交替填的
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6+100;
char s[maxn];
char ch[2] = {'L','R'};
int main()
{
int t;
scanf("%d",&t);
while (t--)
{
int cur = 0;
int n;scanf("%d",&n);
scanf("%s",&s);
for (int i=0;i<n;++i)
{
if (s[i]=='U')printf("D");
else if (s[i]=='D')printf("U");
else
{
printf("%c",ch[cur]);
cur^=1;
}
}printf("\n");
}
}
B.分类讨论
因为 m e x mex mex的限制, 0 0 0到 a − 1 a-1 a−1必须包含
- 0 ⊕ 1 ⊕ 2 ⊕ ⋯ ⊕ a − 1 = = b 0\oplus1\oplus2\oplus\dots\oplus a-1==b 0⊕1⊕2⊕⋯⊕a−1==b:答案就是 a a a
- 0 ⊕ 1 ⊕ 2 ⊕ ⋯ ⊕ a ! = b : 0\oplus1\oplus2\oplus\dots\oplus a!=b: 0⊕1⊕2⊕⋯⊕a!=b:答案就是 a + 1 a+1 a+1
- 0 ⊕ 1 ⊕ 2 ⊕ ⋯ ⊕ a = = b : 0\oplus1\oplus2\oplus\dots\oplus a==b: 0⊕1⊕2⊕⋯⊕a==b:答案为 a + 2 a+2 a+2
#include<bits/stdc++.h>
using namespace std;
const int maxn = 3e5+100;
int pre[maxn];
int main()
{
for (int i=1;i<maxn;++i)pre[i]=pre[i-1]^i;
int t;scanf("%d",&t);
while (t--)
{
int a,b;scanf("%d %d",&a,&b);
if (pre[a-1]==b)printf("%d\n",a);
else if (pre[a]==b)printf("%d\n",a+2);
else printf("%d\n",a+1);
}
}
C.思维
进位相隔 1 1 1使得一切都变得麻烦了
可以发现,奇数位是绝对不会进位到偶数位上的
奇偶是相对独立的
因此我们可以 n n n的奇数位和偶数位分别抽出来观察
n 1 n_1 n1 奇数位组成的数
n 2 n_2 n2 偶数位组成的数
对于 n 1 , n 2 n_1,n_2 n1,n2遵循的就是经典的加减法了
因此,将 n n n分成两个数 n u m 1 , n u m 2 num_1,num_2 num1,num2
其实就是将 n 1 , n 2 n_1,n_2 n1,n2分别分为两个数,一个给 n u m 1 num_1 num1一个给 n u m 2 num_2 num2
再减去出现 0 0 0的情况
#include<bits/stdc++.h>
using namespace std;
vector<int> ns;
vector<int> res;
map<int,int> mp,mmp;
int main()
{
int t;scanf("%d",&t);
while (t--)
{
res.clear();
ns.clear();
int n;scanf("%d",&n);
while (n)
{
ns.push_back(n%10);
n/=10;
}reverse(ns.begin(),ns.end());
int tmp1 = 0,tmp2 = 0;
for (int i=0;i<ns.size();i+=2)
tmp1 = tmp1*10+ns[i];
for (int i=1;i<ns.size();i+=2)
tmp2 = tmp2*10+ns[i];
if (tmp1==0)
{
printf("%d\n",tmp2-1);
}
else if (tmp2==0)
{
printf("%d\n",tmp1-1);
}
else
{
printf("%lld\n",1LL*(tmp1+1)*(tmp2+1)-2);
}
}
}
D.思维+贪心
我们想一想,十进制和十一进制有什么区别?
十进制比十一进制更加容易向高位进位
如果我们按照十进制的方式计算向高位进了一位,那么十一进制下我们是赚了的!
进的位数越高我们赚的越多。
因此,这一题我们的策略是:贪心地保留高位!
例如: 211 5 211\ 5 211 5
我们此时有 2 2 2个百位, 1 1 1个十位, 1 1 1个个位
在全部保留地情况下也只能划分为 4 4 4个数,因此,我们要拆位数
位数越高,保留下来我们是越赚地
因此,我们从低位开始去拆,但是个位已经拆到头了
所以我们去拆十位,拆成 9 9 9个个位
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
struct cmp
{
bool operator()(int na,int nb)
{
if (nb==1)return false;
else if (na==1)return true;
return na>nb;
}
};
int main()
{
int t;scanf("%d",&t);
while (t--)
{
priority_queue<int,vector<int>,cmp> que;
int s,n;scanf("%d %d",&s,&n);
int tmp=1;
while (s)
{
int cnt=s%10;
if (cnt)while(cnt--)que.push(tmp);
s/=10;
tmp*=10;
}
while(que.size()<n)
{
int num = que.top();
num/=10;
que.pop();
for (int i=1;i<=10;++i)que.push(num);
}
vector<int> res;
while (!que.empty())
{
res.push_back(que.top());
que.pop();
}
sort(res.begin(),res.end());
reverse(res.begin(),res.end());
while (res.size()>n)
{
int sum = res.back();
res.pop_back();
sum += res.back();
res.pop_back();
res.push_back(sum);
}
for (int num:res)printf("%d ",num);
printf("\n");
}
}
E.线段树+思维
看题目的形式我们便会发现,这题我们八成是要上线段树了。
考虑线段树维护答案!
对于线段树的每个节点 r t : [ l , r ] rt:[l,r] rt:[l,r]
我们维护三个值
- s u m [ r t ] : c o u n t ( { q , p } ∣ l ≤ q ≤ p ≤ r a n d a q ≤ a q + 1 ≤ ⋯ ≤ a p ) sum[rt]:count(\{q,p\}|l\le q\le p\le r\ and\ a_q\le a_{q+1}\le \dots \le a_p) sum[rt]:count({q,p}∣l≤q≤p≤r and aq≤aq+1≤⋯≤ap)
- l e 1 [ r t ] : c o u n t ( q ∣ l ≤ q ≤ r a n d a l ≤ a l + 1 ≤ ⋯ ≤ a q ) le1[rt]:count(q|l\le q\le r\ and\ a_l\le a_{l+1}\le \dots \le a_q) le1[rt]:count(q∣l≤q≤r and al≤al+1≤⋯≤aq)
- l e 2 [ r t ] : c o u n t ( q ∣ l ≤ q ≤ r a n d a q ≤ a q + 1 ≤ ⋯ ≤ a r le2[rt]:count(q|l\le q\le r\ and\ a_q\le a_{q+1}\le \dots\le a_r le2[rt]:count(q∣l≤q≤r and aq≤aq+1≤⋯≤ar
也就是说, s u m [ r t ] sum[rt] sum[rt]为答案, l e 1 [ r t ] le1[rt] le1[rt]为以 a [ l ] a[l] a[l]为开头在 [ l , r ] [l,r] [l,r]中最长不下降子序列的长度
l e 2 [ r t ] le2[rt] le2[rt]为以 a [ r ] a[r] a[r]为结尾在 [ l , r ] [l,r] [l,r]中最长不下降子序列的长度
利用这三个值,我们可以更新出他们父节点区间的答案
重点说明一下区间合并答案 [ l , r ] [l,r] [l,r]
我们合并KaTeX parse error: Undefined control sequence: \[ at position 1: \̲[̲l,mid\](rt_1),\…的答案,记为 a n s ans ans
首先 a n s + = s u m [ r t 1 ] + s u m [ r t 2 ] ans+=sum[rt_1]+sum[rt_2] ans+=sum[rt1]+sum[rt2]是肯定的
然后我们要计算跨区间的不下降序列的个数:KaTeX parse error: Undefined control sequence: \* at position 21: …d]\le a[mid+1])\̲*̲le2[rt_1]\*le1[…
即:KaTeX parse error: Undefined control sequence: \* at position 48: …] \le a[mid+1])\̲*̲le2[rt_1]\*le1[…
而, l e 1 , l e 2 le1,le2 le1,le2的更新就更加简单了
至于 u p d a t e update update因为是单点修改,所以没有难度,直接递归到底层然后一路重建即可
关键是 q u e r y query query 大体的步骤和上面建造树时合并KaTeX parse error: Undefined control sequence: \[ at position 1: \̲[̲l,mid\](rt_1),\…答案类似
KaTeX parse error: Undefined control sequence: \* at position 47: …d]\le a[mid+1])\̲*̲le2[rt_1]\*le1[…
但是要注意的是,这里的 l e 2 [ r t 1 ] , l e 1 [ r t 2 ] le2[rt_1],le1[rt_2] le2[rt1],le1[rt2]很可能是错的
因为我们所要求的并不是区间 [ l , r ] [l,r] [l,r]的答案,而是 [ q l , q r ] [ql,qr] [ql,qr]的答案
其中 l ≤ q l ≤ m i d < m i d + 1 ≤ q r ≤ r l\le ql\le mid< mid+1\le qr\le r l≤ql≤mid<mid+1≤qr≤r
因此, l e 2 [ r t 1 ] le2[rt_1] le2[rt1]改为 m i n ( l e 2 [ r t 1 ] , m i d − q l + 1 ) min(le2[rt_1],mid-ql+1) min(le2[rt1],mid−ql+1)
l e 1 [ r t 2 ] le1[rt_2] le1[rt2]改为 m i n ( l e 1 [ r t 2 ] , q r − m i d ) min(le1[rt_2],qr-mid) min(le1[rt2],qr−mid)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5+100;
int n,q;
struct stree
{
int a[maxn];
ll sum[maxn<<2];
int le1[maxn<<2],le2[maxn<<2];
inline void push_up(int rt,int l,int r)
{
int mid = l+r>>1;
sum[rt]=sum[rt<<1|1]+sum[rt<<1];
le1[rt] = le1[rt<<1];
le2[rt] = le2[rt<<1|1];
if (a[mid+1]>=a[mid])
{
sum[rt] += 1LL*le2[rt<<1]*le1[rt<<1|1];
if (le1[rt<<1]==mid-l+1)le1[rt] += le1[rt<<1|1];
if (le2[rt<<1|1]==r-mid)le2[rt] += le2[rt<<1];
}
}
void build(int rt,int l,int r)
{
if (l==r)
{
sum[rt]=1;
le1[rt]=le2[rt]=1;
return;
}int mid = l+r>>1;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
push_up(rt,l,r);
}
void update(int rt,int l,int r,int tar,int val)
{
if (l==r)
{
a[tar]=val;
return;
}
int mid = l+r>>1;
if (mid>=tar)update(rt<<1,l,mid,tar,val);
else update(rt<<1|1,mid+1,r,tar,val);
push_up(rt,l,r);
}
ll query(int rt,int l,int r,int ql,int qr)
{
if (l>=ql&&r<=qr)return sum[rt];
int mid = l+r>>1;
if (mid>=qr)return query(rt<<1,l,mid,ql,qr);
if (mid+1<=ql)return query(rt<<1|1,mid+1,r,ql,qr);
ll ans = query(rt<<1,l,mid,ql,qr)+query(rt<<1|1,mid+1,r,ql,qr);
int len2 = min(mid-ql+1,le2[rt<<1]);
int len1 = min(qr-mid,le1[rt<<1|1]);
if (a[mid+1]>=a[mid])
ans+=1LL*len1*len2;
return ans;
}
}S1;
int main()
{
ios::sync_with_stdio(0);
cin>>n>>q;
for (int i=1;i<=n;++i)cin>>S1.a[i];
S1.build(1,1,n);
while (q--)
{
int opt;cin>>opt;
if (opt==1)
{
int x,y;cin>>x>>y;
S1.update(1,1,n,x,y);
}
else
{
int ql,qr;cin>>ql>>qr;
cout<<S1.query(1,1,n,ql,qr)<<endl;
}
}
}