A.
题目大意:
x坐标上1~L有L个点都是整数,每v个长度就有一个灯亮着,但是有 [ l , r ] 这段区间上有列火车挡住了,问你能看到多少亮灯。
解题报告:
大水题啊,找几个样例就会发现需要特殊处理一下左边界恰好有灯的情况。
AC代码:
#include<bits/stdc++.h>using namespace std;
int L,v,l,r,ans;
int main()
{int t;cin>>t;while(t--) {scanf("%d%d%d%d",&L,&v,&l,&r);ans = (L/v) - (r/v - l/v);if(l%v==0) ans--;printf("%d\n",ans);}return 0 ;
}
B.
题目大意:
一个大小为n的数组代表n个房间,数组第i个元素为1代表在第i个房间有一个热水器,可以热给范围为[i-r+1,i+r-1]范围的房间热水,问最少需要多少热水器,可以供给所有房间。
解题报告:
模拟一下过程就好了,记得终点不是在n,而是要加一个常数。因为有那一步i=cur,所以时间不是线性的,而是近似o(n^2)了。
AC代码:
#include<bits/stdc++.h>using namespace std;
int a[100005];
int main()
{int n,r,flag=1;cin>>n>>r;r--;for(int i = 1; i<=n; i++) scanf("%d",a+i);int cur = 0,now = 0,cnt = 0;int pos = 1;for(int i = 1; i<=n+2000; i++) {if(pos > n) break;if(a[i] == 1) {if(i-pos <= r) {now = i;}}if(i-pos > r) {if(now == cur) {flag=0;break;}cnt++;cur=now;pos = cur + r + 1;i=cur;}}if(flag == 0) puts("-1");else printf("%d\n",cnt);return 0 ;
}
另附一种模拟思路:(就是彻彻底底的o(n^2)了,每次都从末尾开始找,找到的第一个满足条件的一定是我们喜欢的,不过反正n很小,n^2也不怂,主要是这种思路不容易写萎了啊)
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAX = 1e3+5;
int n,r;
int a[MAX];
int main()
{scanf("%d%d",&n,&r);for(int i=1; i<=n; i++)scanf("%d",&a[i]);int last = 0,num = 0;bool flag;int pos;while(1) {if(last>=n) break; flag = false;int pp = max(last-r+1,0);for(int i=n; i>pp; i--) {if(a[i]) {if(i-r<=last) {pos = i;flag = true;break;}}}if(!flag) {printf("-1\n");return 0;}last = pos+r-1;num++;}printf("%d\n",num);return 0;
}
wjh的代码:
#include<bits/stdc++.h>
#include<queue>
using namespace std;
int a[2000];
int main() {int n,r;scanf("%d%d",&n,&r);for(int i=1; i<=n; i++) {scanf("%d",&a[i]);}int d=r;int flag=0;for(int i=r; i>=1; i--) {if(a[i]==1) {flag=i;break;}}int ans=1;if(flag==0) {printf("-1\n");} else {int wi=flag;int ff=0;while(wi+r-1<n) {d=wi+r+r-1;ff=0;for(int i=d; i>wi; i--) {if(a[i]==1) {ff=1;wi=i;ans++;break;}}if(ff==0)break;}if(wi+r-1>=n)printf("%d\n",ans);else {printf("-1\n");}}return 0;
}
刚开始 在最后判断的时候写成了if(ff=0),就错了,因为满足不了第一次就完成的 就进不去while了
C.
题目大意:
开始空队列,三种操作,可以进行前插和后插,查询使某个数的为最左或最右需要去掉的最少数字(其实也就是距离队列最左端或者最右端哪个更近)。
解题报告:
这题如果想着,每次加入一个答案都去维护之前的所有的值,那就走远了。这题完全可以记录一下位置然后中间完全不用维护,最后直接输出结果就可以了。
#include<bits/stdc++.h>using namespace std;
int q,l,r,tmp;
char op[5];
int a[500005];
int main()
{cin>>q;l = 0,r = 0;for(int i = 1; i<=q; i++) {scanf("%s",op);if(op[0] == 'L') {scanf("%d",&tmp);a[tmp] = --l;if(i == 1) r--;}else if(op[0] == 'R') {scanf("%d",&tmp);a[tmp] = ++r;if(i == 1) l++;}else {scanf("%d",&tmp);printf("%d\n",min(r-a[tmp],a[tmp]-l));}}return 0 ;
}
D.
题目大意:
有n个物品,、第一给物品开始往右边挑选,对于每一个物品,如果可以放得下的就直接放进盒子,若目前的盒子放不下,那就要另外开一个盒子来放物品了,若目前还有可用的盒子,那就继续选取下去,若目前已经没有可用的盒子了,说明这次选取是不对的,那么选取的第一个物品退回去,看是否有剩余的空间放下该物品,如果还是放不下的话,继续把第二个拿的物品退回去,直至可以放下该物品为止,然后继续选取下去。
解题报告:
读懂题了就很简单,就是往盒子里按照给定的顺序放东西,如果放满了就新开一个盒子放东西,如果都满了,那就把最早放入的物品拿出来,然后继续放后面的物品,直到放到最后一个物品。问你盒子中最多有多少物品?。。。就是这一句话,好有迷惑性,一般都会感觉是中间过程也会算上,而且不能打乱顺序放,所以不能排序,就是必须按照他的algorithm放东西,所以就不好模拟了啊!但是其实题意是就问你最后盒子中物品的数量,而不是中间某个过程就算物品很多,也被拿出来了,那也 白搭,所以只和最终状态有关,,这样正着还是不好模拟,所以采用时光倒流法,,,倒着模拟,就简单多了,或者先二分从第几个盒子开始装,然后再验证是否可行即可(也可以二分最终装了多少个物品,不过其实一样的因为反正都是倒着装)。不过这个题的二分没啥乱用,,因为直接模拟就出结果了。
#include <bits/stdc++.h>
#define ll long longusing namespace std;
int n,box,siz;
int a[200005];bool ok(int mid){int k=1,rem=siz;for(int i = mid; i<=n; i++){if(rem<a[i]){rem=siz;k++;}if(k>box) return 0;rem-=a[i];}return 1;
}
int main()
{cin>>n>>box>>siz;for(int i = 1; i<=n; i++) cin>>a[i];int mid,l=1,r=n,ans;while(l<=r){mid=(l+r)/2;if(ok(mid))ans=mid,r=mid-1;else l=mid+1;}cout << n + 1 -ans << endl;return 0;
}
E.
题目大意:
给你长度分别为n,m的二进制串,当b>0时,对a,b,&运算结果为tmp,然后b右移一位,把每次a&b的10进制结果(tmp)累加得到sum,结果输出sum对998244353取余的值。
解题报告:
模拟过程不难发现,a的每一个位置做出的贡献次数,就是b对应位置前缀1的个数,于是维护一下就好了。(因为b字符串一直在变,但是a是固定不变的啊!所以我们预处理一下a字符串)
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n,m;
const ll mod = 998244353;
char op[5];
char a[500005],b[500005];
ll ans[500005];
ll qpow(ll a,ll k) {ll res = 1;while(k) {if(k&1) {res *= a;res %= mod;}k>>=1;a=(a*a)%mod;}return res;
}
int main()
{ll cha = 0;cin>>n>>m;cin>>(a+1);cin>>(b+1);ll lena = strlen(a+1);ll lenb = strlen(b+1);ll cur = 0;if(strlen(a+1) < strlen(b+1)) {cha = strlen(b+1) - strlen(a+1);for(ll i = 1; i<=cha; i++) {if(b[i] == '1') cur++;}for(ll i = 1; i<=strlen(a+1); i++) {if(b[i+cha]=='1') ans[i] = ++cur;else ans[i]=cur;}}else {cha = strlen(a+1) - strlen(b+1);for(ll i = 1; i<=strlen(b+1); i++) {if(b[i] == '1') ans[i+cha] = ++cur;else ans[i+cha] = cur;}}ll out = 0;for(ll i = 1; i<=strlen(a+1); i++) {if(a[i] == '1') {out += (ll)ans[i] * qpow(2,lena-i);out %= mod;}}printf("%lld\n",out%mod);return 0 ;
}
比赛的时候忘记考虑a比b长的情况了、、不然这场比赛完美了啊。 最后压哨失败很难受。。
这道题还有更精彩更简洁的解法,把两个字符串处理成相同长度的,再去维护,岂不是美滋滋?正好用到了string类,重载 ' + ' 焦作人啊!另外,这个代码中预处理了2^n,做到了o(1)出结果,比快速幂的log要好一些。
AC代码2:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod=998244353;
const ll N=2e5+10;
ll one[N],pw[N];
int main()
{ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); ll i,j,k,n,m;string a,b;cin>>n>>m;cin>>a>>b;//使位数一致在位数少的前面加0 if(n<m){a=string(m-n,'0')+a;}else if(m<n){b=string(n-m,'0')+b;}n=max(n,m);one[0]=(b[0]=='1');for(i=1;i<n;i++) one[i]=one[i-1]+(b[i]=='1');//b高位开始的前缀和 pw[0]=1;for(i=1;i<N;i++) pw[i]=pw[i-1]*2ll%mod;//计算2的幂次预处理 ll ans=0;for(i=0;i<n;i++){ll res=(a[i]=='1');res=res*pw[n-i-1]%mod;//计算a这个位置的10进制值 ans=(ans+res*one[i]%mod)%mod;//之所以*one[i],是因为b右移的过程,a[i]对应的次数就是b高位开始的前缀和,注意取余mod }cout<<ans<<endl;return 0;
}