T1
题目:定义 Ci,jC_{i,j}Ci,j
Ci,j={max(i,j)i=1∧j=1Ci−1,j+Ci,j−1+i+j2otherwiseC_{i,j}=\begin{cases}\operatorname{max}(i,j)&i=1\wedge j=1\\\frac{C_{i-1,j}+C_{i,j-1}+i+j}{2}&\operatorname{otherwise}\end{cases}Ci,j={max(i,j)2Ci−1,j+Ci,j−1+i+ji=1∧j=1otherwise
求 ∑i=1n∑j=1mCi,j\sum^n_{i=1}\sum^m_{j=1}C_{i,j}∑i=1n∑j=1mCi,j。答案对 100000000710000000071000000007 取模。
很水的一道题,画几个图表找规律就行,规律自己找。
T2
题目:给定一个长度为 nnn 的序列 AiA_iAi。定义 Wl,rW_{l,r}Wl,r 为 Al,Al+1,⋯ ,Ar−1,ArA_l,A_{l+1},\cdots,A_{r−1},A_rAl,Al+1,⋯,Ar−1,Ar 内出现奇数次的元素的异或和。例如序列 A=3,5,3,6,7A={3,5,3,6,7}A=3,5,3,6,7,W1,4=5⊕6=3W_{1,4}=5\oplus6=3W1,4=5⊕6=3。现在给定 qqq 次询问,每次询问给出两个参数l,r,求出 ⊕i=lr⊕j=irWi,j\oplus^r_{i=l}\oplus^r_{j=i}W_{i,j}⊕i=lr⊕j=irWi,j,即每个区间的 Wl,rW_{l,r}Wl,r 的异或和。
我们假设 Pl,r=⊕i=lr⊕j=irWi,jP_{l,r}=\oplus^r_{i=l}\oplus^r_{j=i}W_{i,j}Pl,r=⊕i=lr⊕j=irWi,j,则:
我们来举几个例子看看:
当 l=1,r=1l=1,r=1l=1,r=1 时,P1,1=W1,1P_{1,1}=W_{1,1}P1,1=W1,1。
当 l=1,r=2l=1,r=2l=1,r=2 时,P1,2=0P_{1,2}=0P1,2=0。
当 l=1,r=3l=1,r=3l=1,r=3 时,P1,3=W1,1⊕W3,3P_{1,3}=W_{1,1}\oplus W_{3,3}P1,3=W1,1⊕W3,3。
当 l=1,r=4l=1,r=4l=1,r=4 时,P1,4=0P_{1,4}=0P1,4=0。
当 l=1,r=5l=1,r=5l=1,r=5 时,P1,5=W1,1⊕W3,3⊕W5,5P_{1,5}=W_{1,1}\oplus W_{3,3}\oplus W_{5,5}P1,5=W1,1⊕W3,3⊕W5,5。
当 l=2,r=4l=2,r=4l=2,r=4 时,P2,4=W2,2⊕W4,4P_{2,4}=W_{2,2}\oplus W_{4,4}P2,4=W2,2⊕W4,4。
这时我们能很明显的看出如下规律:
Pl,r={Wl,l⊕Wl+2,l+2⊕⋯⊕Wr,rl+r为偶数0l+r为奇数P_{l,r}=\begin{cases}W_{l,l}\oplus W_{l+2,l+2}\oplus\cdots\oplus W_{r,r}&l+r\text{为偶数}\\0&l+r\text{为奇数}\end{cases}Pl,r={Wl,l⊕Wl+2,l+2⊕⋯⊕Wr,r0l+r为偶数l+r为奇数
具体证明我目前不知道,感兴趣的同学可以自己试试看。
那我们根据上面的规律写个异或前缀和就行了;
注意:这里需要奇偶两个前缀和,不然会混乱的。
代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,q,l,r,cnt=1,cntt=1,a[200006],s[200006],ss[200006];
signed main() {
// freopen("dragon.in","r",stdin);
// freopen("dragon.out","w",stdout);
cin>>n>>q;
for(int i=1; i<=n; i++) {
cin>>a[i];
}
for(int i=1; i<=n; i+=2) {
s[cnt]=s[cnt-1]^a[i];
cnt++;
}
for(int i=2; i<=n; i+=2) {
ss[cntt]=ss[cntt-1]^a[i];
cntt++;
}
while(q--) {
cin>>l>>r;
if((l+r)&1) {
cout<<0<<endl;
} else {
if(l&1) {
cout<<(s[(r+1)>>1]^s[((l+1)>>1)-1])<<endl;
} else {
cout<<(ss[r>>1]^ss[(l>>1)-1])<<endl;
}
}
}
return 0;
}
T3
题目:有一个足球球队了 nnn 场比赛,每场比赛有胜利、平局、失败 333 种情况。比赛会进行小组积分,胜利加 333 分,平局加 111 分,分数不变。为了让积分更多,你使用了最强大脑预测了比赛情况,其中,第 iii 场比赛我方进了 AiA_iAi 个球,对方进了 BiB_iBi 个球。现在你可以针对预测的比赛对队员进行强化,每次强化用在一场比赛上,可以让这场比赛多进一个求,每场比赛可以强化多次或者不强化,并以总共只能强化 mmm 次,问最多能积分多少?注意:你的某次强化并不会干扰其他比赛进程,可以看作每场比赛和队员都互相独立。
很典型的一道贪心,感觉没什么可讲的。就简单提一嘴吧。
首先肯定要把我方实力和对方实力作差,接着从小到大排序,如果小于 000,表明我方已经胜利,就不用管,如果等于 000,那么就多花费一次机会去提升实力,然后赚 333 分,如果大于 000,就自己推贪心,我不想说了 (懒)。
代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
struct qd {
int x,y,c;
} a[200006];
int n,m,ans;
bool cmp(qd x,qd y) {
return x.c<y.c;
}
signed main() {
// freopen("starship.in","r",stdin);
// freopen("starship.out","w",stdout);
cin>>n>>m;
for(int i=1; i<=n; i++) {
cin>>a[i].x>>a[i].y;
a[i].c=a[i].y-a[i].x;
}
sort(a+1,a+n+1,cmp);
for(int i=1; i<=n; i++) {
if(a[i].c<0) {
ans+=3;
continue;
} else if(a[i].c==0) {
if(m) {
m--;
ans+=3;
} else {
ans++;
}
} else {
if(m) {
m-=a[i].c;
if(m<0);
else if(m==0) {
ans++;
} else {
m--;
ans+=3;
}
} else;
}
}
cout<<ans;
return 0;
}
T4
题目:定义一系列数组 A0,A1,A2,A3⋯A_0,A_1,A_2,A_3\cdotsA0,A1,A2,A3⋯
An={{0}n=0{0,1}n=1{An−1,n,An−2}otherwiseA_n=\begin{cases}\{0\}&n=0\\\{0,1\}&n=1\\\{A_{n-1},n,A_{n-2}\}&\operatorname{otherwise}\end{cases}An=⎩⎨⎧{0}{0,1}{An−1,n,An−2}n=0n=1otherwise
例如:
A0={0}A1={0,1}A2={0,1,2,0}A3={0,1,2,3,0,1}⋯\begin{split}&A_0=\{0\}\\&A_1=\{0,1\}\\&A_2=\{0,1,2,0\}\\&A_3=\{0,1,2,3,0,1\}\\&\cdots\end{split}A0={0}A1={0,1}A2={0,1,2,0}A3={0,1,2,3,0,1}⋯
现在请你输出 A99824353A_{99824353}A99824353 的 lll 到 rrr 项是什么。(1≤l,r≤10181\le l,r\le10^{18}1≤l,r≤1018)
一道比较简单的题。知识点:分治。
观察上面那个式子不难发现:每个数组里面都有前面的成分 (成分复杂……),所以很容易想到分治,至于怎么分呢,这我得好好讲讲了。
首先我们肯定要从 nnn 分开,那对于 nnn 的位置和当前数组的长度有什么规律呢?请看 VCR 下列样例(皮一下很开心):
通过仔细的观察很容易发现:nnn 的位置是按照斐波那契数列的规律排列的,长度则是下一个 nnn 的位置 −1-1−1 得到的,即(idiid_iidi 表示 iii 在第几个位置):
leni=idi+1−1=idi+idi−1−1len_i=id_{i+1}-1=id_i+id_{i-1}-1leni=idi+1−1=idi+idi−1−1
而根据上面的式子,我们可以知道:
idi=leni−1+1id_i=len_{i-1}+1idi=leni−1+1
带入上面的式子可以得到:
leni=leni−1+1+leni−2+1−1=leni−1+leni−2+1len_i=len_{i-1}+1+len_{i-2}+1-1=len_{i-1}+len_{i-2}+1leni=leni−1+1+leni−2+1−1=leni−1+leni−2+1
所以我们就推导出来了长度的递推方程。然后就可以预处理出长度和位置了。
讲了半天,究竟怎么分治的?其实到这已经很简单了,我们对于从 lll 到 rrr 的每个点枚举一下,然后通过分治找,直到找到了这个点的值就行了。
但有个问题:每个数组里面都会有这个数,我应该在哪个数组里面找这个数呢?这也很好想,只要这个点被移动到了 nnn 的位置就行了。
最后一个问题:求 A99824353A_{99824353}A99824353 中 lll 到 rrr 的值,数组开不下怎么办?其实我们会看到这个点:1≤l,r≤10181\le l,r\le10^{18}1≤l,r≤1018。所以长度的最大值就是 101810^{18}1018,再往上算就没有意义了,纯属浪费空间,只需要再小小的写个代码,求出第几个的时候长度超过了 101810^{18}1018 就行了。
代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
int l,r,len[96],xx[96];//数组我是算过的
signed main() {
len[1]=1,len[2]=2,xx[1]=1,xx[2]=2;
for(int i=3; i<=90; i++) {//预处理
len[i]=len[i-1]+len[i-2]+1;
xx[i]=xx[i-1]+xx[i-2];
}
cin>>l>>r;
int t;
for(int i=1; i<=90; i++) {//找到第一个包含l到r的区间
if(len[i]>r) {
t=i;
break;
}
}
int id;
while(l<=r) {
int pos=t;
id=l++;//枚举每一位
while(id) {//分治
if(id==xx[pos]) {//找到了
break;
}
if(id>xx[pos]) {//在右边
id-=xx[pos];
pos-=2;
} else if(id<xx[pos]) {//在左边
pos--;
}
}
cout<<pos-1<<" ";
}
return 0;
}