做了4题,ABCE。感觉不是很满意。1、D题题意读不懂,刚开始想法对了,但是可惜题意读错了一直WA。2、F题没来得及看。
感觉本场比赛的题目还算可以。
A:三行系列。读懂题意就能做。因为只除去了中间的部分。但是感觉放在div3的A题还是稍微难了点。
#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=200010;
int n,m,k,v;
int a[maxn],sum[maxn];
int c[maxn];
int ans,ct,cnt,tmp,flag;
char s[maxn];
int main()
{
int T,cas=1;
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d%d",&n,&m,&k,&v);
ans=n/m;
tmp=v/m-(k-1)/m;
printf("%d\n",ans-tmp);
}
return 0;
}
B:感觉这题有点坑。给你一01序列,长度为n,再给你r,1的地方可以种洒水器,范围是(pos-r+1,pos+r-1)。pos是为1的位置。
问你至少种多少洒水器可以覆盖1~n。如果不能输出-1。直接模拟即可。不过细节挺多,样例给的还算好。注意-1。
#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=200010;
int n,m,k,v;
int a[maxn],sum[maxn];
int c[maxn];
int ans,ct,cnt,tmp,flag;
char s[maxn];
int main()
{
int T,cas=1;
scanf("%d%d",&n,&m);
ans=-1;
for(int i=1;i<=n;i++)
{scanf("%d",&a[i]);if(a[i])ans=0;}
int r=0;
int i=1;
while(i<=n)
{
int tmp=ans,fg=0;
for(;i<=n;i++)
if(a[i]&&i-m+1<=r+1){fg=i;}
else if(a[i]&&i-m+1>r+1) break;
if(fg) ans++;
r=fg+m-1;
i=fg+1;
if(r>=n) break;
if(!fg) {ans=-1;break;}
}
printf("%d\n",ans);
return 0;
}
C:有一个书架(只有一层),m次操作,每次操作为 一个字符+x 如果字符为'L'或'R',则放上编号为x的书。放在最左边或最右边。如果是'?' x 则查询至少移除多少本书才能使编号为x的书位于最左边或最右边。
不知道别人思路怎么样,我的第一感觉就是树状数组。从中间分开,200005和200006开始分别往左右放,询问的时候查询啷个值比较一下就行了。
#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=400010;
int n,m,k,v;
int a[maxn],mp[maxn];
int c[maxn];
int ans,ct,cnt,tmp,flag;
char s[maxn];
int lb(int x){return x&(-x);}
void add(int i,int v)
{
while(i<maxn)
{
c[i]+=v;
i+=lb(i);
}
}
int query(int i)
{
int ans=0;
while(i>0)
{
ans+=c[i];
i-=lb(i);
}
return ans;
}
int main()
{
int T,cas=1;
scanf("%d",&n);
memset(c,0,sizeof(c));
int l=200005,r=200006;
for(int i=0;i<n;i++)
{
scanf("%s %d",s,&m);
if(s[0]=='L') {mp[m]=l--;add(l+1,1);}
else if(s[0]=='R') {mp[m]=r++;add(r-1,1);}
else
{
ans=min(query(mp[m]-1),query(400008)-query(mp[m]));
printf("%d\n",ans);
}
}
// if(flag) puts("Yes"); else puts("No");
return 0;
}
D:这题题意很迷啊。我感觉题意说的有问题。实际上就是给你n个玩具,m个箱子,每个箱子容量为k,每个玩具体积为a[i]。
从第一个玩具开始依次装,如果能把所有玩具装进去就停止。否则到了一个玩具,已经没有足够的箱子容量装了的话,他就会把现在所有箱子里最早装的玩具扔掉。直到当前玩具能装进去。问最后箱子里一共能装多少玩具。
思路:如果题意像我说的这么简单,那么这道题就可以当做A题了(然而出题人非要整的花里胡哨乱七八糟看不懂的)。显然只需要从后往前贪心装,装不下就停。这时装的玩具数量就是答案。
#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=400010;
ll n,m,k,v;
ll a[maxn],sum[maxn];
ll c[maxn],ed[maxn],be[maxn];
ll ans,ct,cnt,tmp,flag;
char s[maxn];
int main()
{
int T,cas=1;
scanf("%lld%lld%lld",&n,&m,&k);
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
}
for(int i=0;i<=max(2*n,2*m);i++)
c[i]=k;
int box=0,l=1,ans=0,tmp=0,st=0,aa=0;
for(int i=n;i>=1;i--)
{
if(c[box]>=a[i])
{
c[box]-=a[i];
be[i]=box;
tmp++;
ans=max(ans,tmp);
}
else if(box+1<m)
{
box++;
c[box]-=a[i];
be[i]=box;
tmp++;
ans=max(ans,tmp);
}
else break;
}
printf("%d\n",ans);
// if(flag) puts("Yes"); else puts("No");
return 0;
}
E:
给你两个二进制01串,长度分别为n,m(n,m<=2e5)。求第一个串&第二个串的十进制和。每&一次,就去掉第二个串的最后一位继续&,直到第二个串为空。
观察可以发现,只需要维护一个2的n次方的前缀和就行了。m>n时,第二个串的前m-n位一定是每一个1都会与第一个串每一个1&一次,后面的n位就依次往后了。m<n时,第二个串每个1就只有机会&第一个串中后n-m个1了。可以画图理解一下。(题意说清楚就比D题简单了)
我用的快速幂,其实可以不用的,不用就可以O(n)解决。
#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=400010;
const ll mo=998244353;
ll n,m,k,v;
ll a[maxn],sum[maxn];
ll c[maxn],ed[maxn],be[maxn];
ll ans,ct,cnt,tmp,flag;
char s[maxn],t[maxn];
ll power(ll a,ll n) //a的n次方mod
{
ll ans=1;
a=a%mo;
while (n)
{
if(n&1) ans=(ans*a)%mo;
n>>=1;
a=(a*a)%mo;
}
return ans;
}
int main()
{
int T,cas=1;
scanf("%lld%lld",&n,&m);
scanf("%s%s",s,t);
sum[n+1]=0;ans=0;
for(int i=n-1;i>=0;i--)
{
if(s[i]=='0') sum[i]=sum[i+1];
else sum[i]=(sum[i+1]+power(2,n-i-1))%mo;
}
if(m>n)
{
for(int i=0;i<m-n;i++)
if(t[i]=='1'){
ans=(ans+sum[0])%mo;
}
for(int i=m-n,j=0;i<m;i++,j++)
if(t[i]=='1'){
ans=(ans+sum[j])%mo;
}
}
else
{
for(int i=0,j=n-m;i<m;i++,j++)
if(t[i]=='1'){
ans=(ans+sum[j])%mo;
}
}
printf("%lld\n",ans);
// if(flag) puts("Yes"); else puts("No");
return 0;
}
F:(这道题在区域赛上可能算个签到题吧。。。)
给你二维平面的n个点(n<=2e5) 以每个点的max(x,y)分层,你从(0,0)点开始走,每一步只能走上下左右长度为1,你要先走到第一层,走过第一层的所有点后,才能走第二层,以此类推,求走完最后一层的所有点,最少需要多少步。
由于要分层,就用一个vector存一下每一层的点。观察发现每一层最左上的点到最右下的点,一定有一条路径可以经过这一层的所有点。显然这就是走遍这一层最短步数路线。于是每一层的点,按x从小到大排序,x相同按y从大到小排序。画个图一下就懂了。于是dp方程也有了。设dp[i][0]为走完第i层且位于这一层的最左上的点的最少步数。则v[i][]表示第i层的所有点(排序后的)
t1和t2为离散化后第i-1和第i层的点的编号。于是有
dp[i][0]=min(dp[i-1][1]+dist(v[t1].back(),v[t2].back()),dp[i-1][0]+dist(v[t1][0],v[t2].back()))+dist(v[t2][0],v[t2].back());
同样,设dp[i][1]为走完第i层且位于这一层的最右下的点的最少步数。则有
dp[i][1]=min(dp[i-1][1]+dist(v[t1].back(),v[t2][0]), dp[i-1][0]+dist(v[t1][0],v[t2][0]))+dist(v[t2][0],v[t2].back());
最后min(dp[最后一层][0],dp[最后一层][1])就是答案。
#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f3f3f3f3fLL
using namespace std;
const int maxn=1e6+10;
map<int,int>mp;
int cnt,n;
vector<pair<ll,ll> >v[maxn];
ll dp[maxn][2];
ll dist(pair<ll,ll> a,pair<ll,ll> b)
{
return abs(a.first-b.first)+abs(a.second-b.second);
}
bool cmp(pair<ll,ll> a,pair<ll,ll> b)
{
return a.first<b.first||(a.first==b.first&&a.second>b.second);
}
int main()
{
scanf("%d",&n);
vector<ll>vv;
cnt=0;
for(int i=1;i<=n;i++)
{
ll l,r;
scanf("%lld%lld",&l,&r);
ll c=max(l,r);
if(mp[c]==0)
{
mp[c]=++cnt;
vv.push_back(c);
}
v[mp[c]].push_back(make_pair(l,r));
}
vv.push_back(0);
mp[0]=++cnt;
v[cnt].push_back(make_pair(0,0));
sort(vv.begin(),vv.end());
for(int i=0;i<vv.size();i++)
dp[i][0]=dp[i][1]=inf;
dp[0][0]=dp[0][1]=0;
for(int i=1;i<vv.size();i++)
{
int t1=mp[vv[i-1]];
int t2=mp[vv[i]];
sort(v[t2].begin(),v[t2].end(),cmp);
dp[i][0]=min(dp[i-1][1]+dist(v[t1].back(),v[t2].back()),
dp[i-1][0]+dist(v[t1][0],v[t2].back()));
dp[i][0]+=dist(v[t2][0],v[t2].back());
dp[i][1]=min(dp[i-1][1]+dist(v[t1].back(),v[t2][0]),
dp[i-1][0]+dist(v[t1][0],v[t2][0]));
dp[i][1]+=dist(v[t2][0],v[t2].back());
}
printf("%lld\n",min(dp[vv.size()-1][0],dp[vv.size()-1][1]));
}