C题:https://ac.nowcoder.com/acm/contest/700/C
题意:给你n个数,要你求长度在[L,R]之间的区间中有多少个区间的区间和>=S
分析:考虑以点pos作为左端点,其满足条件的区间和为[pos,pos+l-1]到[pos,pos+r-1],转化为前缀和,即为求
x∈[pos+l-1,pos+r-1]的区间内,满足pre[x]−pre[pos−1]≥S的x的数量。
即对于每个点,求解每个区间x∈[pos+l-1,pos+r-1]内pre[x]≥S+pre[pos−1]的数量,可以想到采用树状数组维护[l+pos-1,r+pos-1]区间内存在的前缀和的值的个数,并统计当前区间内前缀和的值在区间[S+pre[pos−1],MAX]的数量。将前缀和离散化后依次插入树状数组更新即可求解。
Ac code:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
typedef long long ll;
int n,l,r;
ll s;
int c[maxn];
ll sorted[maxn],pre[maxn];
int a[maxn];
int lowbit(int x)
{
return x&(-x);
}
void add(int x,int v)
{
while(x<=n){
c[x]+=v;
x+=lowbit(x);
}
}
int getsum(int x)
{
int sum=0;
while(x){
sum+=c[x];
x-=lowbit(x);
}
return sum;
}
int main()
{
scanf("%d%d%d%lld",&n,&l,&r,&s);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
pre[i]=pre[i-1]+a[i];
sorted[i]=pre[i];
}
sort(sorted+1,sorted+n+1);
for(int i=l-1;i<r;i++){///当pos=0时把x属于[pos+l-1,pos+r-1]内的所有前缀和离散化后插入树状数组
int id=lower_bound(sorted+1,sorted+1+n,pre[i])-sorted;
add(id,1);
}
ll ans=0;
for(int i=0;i<=n-l;i++)
{
int idl=lower_bound(sorted+1,sorted+1+n,pre[i+l-1])-sorted;
add(idl,-1);///区间整体往右移一个单位,左边减一
if(i+r<=n){
int idr=lower_bound(sorted+1,sorted+1+n,pre[i+r])-sorted;
add(idr,1);///右边加1
}
int idx=lower_bound(sorted+1,sorted+1+n,s+pre[i])-sorted-1;
ans+=getsum(n)-getsum(idx);
}
printf("%lld\n",ans);
return 0;
}
E题:https://ac.nowcoder.com/acm/contest/700/E
题意:
给定两个阵营A,B,阵营中的人能够直接得到与其曼哈顿距离小于Sa或Sb的人的坐标,且若有同阵营的人在一个人的视野之内,他可以得知这个人所能得到的所有敌方坐标,给定A,B阵营中的人所在的坐标,问能否存在满足以下条件的Sa,Sb: 1. A阵营中的所有人都知道B阵营中所有人的角色位置 2. B阵营中至少存在一个人不知道A阵营中一个人的角色位置 3. Sa<Sb
分析: 为了满足条件2,应该尽可能最小化Sb,又因为条件3,令Sb=Sa+1。即我们需要求得最小的Sa,使Sa满足条件1。二分求解Sa可以取得的最小值,并判断此时的Sb=Sa+1是否满足条件2即可。在求当前S是否可行时用并查集维护间接获得的敌方坐标即可判断其能否得知敌方的所有点。
Ac code:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1005;
struct Point
{
int x,y;
};
Point a[maxn],b[maxn];
int parent[maxn];
bool vis[maxn][maxn];
int fnd(int x)
{
return parent[x]!=x?parent[x]=fnd(parent[x]):x;
}
int dist(Point p1,Point p2)
{
return abs(p1.x-p2.x)+abs(p1.y-p2.y);
}
bool check(int x,Point a[],Point b[],int n,int m)
{
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++)
vis[i][j]=dist(a[i],b[j])<=x;
for(int i=1; i<=n; i++) parent[i]=i;
for(int i=1; i<=n; i++)
for(int j=i+1; j<=n; j++)
{
if(dist(a[i],a[j])>x) continue;
int fx=fnd(i),fy=fnd(j);
if(fx!=fy)
{
parent[fy]=fx;
for(int k=1; k<=m; k++) vis[fx][k]|=vis[fy][k];
}
}
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++)
if(!vis[fnd(i)][j]) return 0;
return 1;
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++)
scanf("%d%d",&a[i].x,&a[i].y);
for(int i=1; i<=m; i++)
scanf("%d%d",&b[i].x,&b[i].y);
int l=0,r=5000,sa=5000;
while(l<=r)
{
int mid=(l+r)>>1;
if(check(mid,a,b,n,m))
{
sa=mid;
r=mid-1;
}
else l=mid+1;
}
printf("%d\n",!check(sa+1,b,a,m,n));
return 0;
}
F题:https://ac.nowcoder.com/acm/contest/700/F
题意:给一个6x6的矩阵,每次放入一个1行3列,2行3列,3行2列,3行1列的方块,如果整行/整列被占据,该行/列将会消失。输出一个能把所给的方块全部放入矩阵中的方案。
分析:一个cf类似的题,就是利用公共位置肯定不会重叠去构造。
Ac code:
#include<bits/stdc++.h>
using namespace std;
char s[500005];
int main()
{
bool v1,v2,v3,v4;
v1=0,v2=0,v3=1,v4=1;
scanf("%s",s);
int len=strlen(s);
for(int i=0;i<len;i++){
if(s[i]=='2') printf("%d %d\n",3*v1+1,1),v1=!v1;
else if(s[i]=='3') printf("%d %d\n",3*v2+1,3),v2=!v2;
else if(s[i]=='1') printf("%d %d\n",5,3*v3+1),v3=!v3;
else printf("%d %d\n",4,3*v4+1),v4=!v4;
}
return 0;
}
H题:https://ac.nowcoder.com/acm/contest/700/H
题意:给定一段由单词组成的句子和一系列可行的转换,使转换后句子的元音字母最小,如果有多种情况,则使句子的总长度最小。输出最小元音个数和句子总长度。
分析:考虑建反向边,对单词按照元音字母和长度升序排序,按序BFS未访问的点,BFS的起点即为当前点可以取得的最优情况。(需要long long)
Ac code:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
int a[2 * maxn], cnt;
string s[maxn];
struct Node{ int val, len; }node[2 * maxn];
bool cmp(const int &a, const int &b) {return node[a].val == node[b].val ? node[a].len < node[b].len : node[a].val < node[b].val; }
int cal(string a)
{
int res = 0;
for(int i = 0; i < a.length();i++)
if(a[i] == 'a' || a[i] =='e' || a[i] == 'i' || a[i] == 'o' || a[i] == 'u') res++;
return res;
}
map<string, int> mp;
void add(string x)
{
if(mp.count(x)) return;
node[cnt].val = cal(x);
node[cnt].len = x.length();
mp[x] = cnt++;
}
vector<int> edge[2 * maxn];
bool vis[2 * maxn];
void BFS(int st)
{
queue<int> que;
que.push(st);
while(!que.empty())
{
int u = que.front();
que.pop();
vis[u] = true;
node[u].val = node[st].val;
node[u].len = node[st].len;
for(auto & v :edge[u]) if(!vis[v]) que.push(v);
}
}
int main()
{
ios::sync_with_stdio(false);
int n, m;
cin >> n;
for(int i = 0; i < n; i++)
{
cin >> s[i];
add(s[i]);
}
cin >> m;
string x, y;
while(m--)
{
cin >> x >> y;
add(x), add(y);
edge[mp[y]].push_back(mp[x]);
}
for(int i = 0; i < cnt; i++) a[i] = i;
sort(a, a + cnt, cmp);
for(int i = 0; i < cnt; i++) if(!vis[a[i]]) BFS(a[i]);
long long res1 = 0, res2 = 0;
for(int i = 0; i < n; i++) res1 += node[mp[s[i]]].val, res2 += node[mp[s[i]]].len;
cout << res1 << ' ' << res2 <<endl;
return 0;
}
I题:https://ac.nowcoder.com/acm/contest/700/I
题意: 给定一个m行n列的矩阵,要求取的子矩阵(正方形)的极差不超过G,求在满足条件情况下的最大边长度L。
分析:j解法一:直接用二维rmq,关键要二分边长,O(n*m*log(n)*log(m))
Ac code:
///预处理复杂度n*m*log(n)*log(m)
///查询O(1)
#include<bits/stdc++.h>
using namespace std;
int val[505][505];
int mm[505];
char dpmin[505][505][9][9];//最小值
char dpmax[505][505][9][9];//最大值
void init()
{
mm[0] = -1;
for(int i = 1; i <= 500; i++)
mm[i] = ((i&(i-1))==0)?mm[i-1]+1:mm[i-1];
}
void initRMQ(int n,int m)
{
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
dpmin[i][j][0][0] = dpmax[i][j][0][0] = val[i][j];
for(int ii = 0; ii <= mm[n]; ii++)
for(int jj = 0; jj <= mm[m]; jj++)
if(ii+jj)
for(int i = 1; i + (1<<ii) - 1 <= n; i++)
for(int j = 1; j + (1<<jj) - 1 <= m; j++)
{
if(ii)
{
dpmin[i][j][ii][jj] = min(dpmin[i][j][ii-1][jj],dpmin[i+(1<<(ii-1))][j][ii-1][jj]);
dpmax[i][j][ii][jj] = max(dpmax[i][j][ii-1][jj],dpmax[i+(1<<(ii-1))][j][ii-1][jj]);
}
else
{
dpmin[i][j][ii][jj] = min(dpmin[i][j][ii][jj-1],dpmin[i][j+(1<<(jj-1))][ii][jj-1]);
dpmax[i][j][ii][jj] = max(dpmax[i][j][ii][jj-1],dpmax[i][j+(1<<(jj-1))][ii][jj-1]);
}
}
}
//查询矩阵最大值
int rmq1(int x1,int y1,int x2,int y2)
{
int k1 = mm[x2-x1+1];
int k2 = mm[y2-y1+1];
x2 = x2 - (1<<k1) + 1;
y2 = y2 - (1<<k2) + 1;
return max(max(dpmax[x1][y1][k1][k2],dpmax[x1][y2][k1][k2]),max(dpmax[x2][y1][k1][k2],dpmax[x2][y2][k1][k2]));
}
//查询矩形的最小值
int rmq2(int x1,int y1,int x2,int y2)
{
int k1 = mm[x2-x1+1];
int k2 = mm[y2-y1+1];
x2 = x2 - (1<<k1) + 1;
y2 = y2 - (1<<k2) + 1;
return min(min(dpmin[x1][y1][k1][k2],dpmin[x1][y2][k1][k2]),min(dpmin[x2][y1][k1][k2],dpmin[x2][y2][k1][k2]));
}
int main()
{
init();///mm数组初始化
int n,m,g;
scanf("%d%d%d",&n,&m,&g);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&val[i][j]);
initRMQ(n,m);
int ans=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
int l=1,r=min(n-i+1,m-j+1),mid;
while(l<=r){
mid=(l+r)>>1;
int _max=rmq1(i,j,i+mid-1,j+mid-1);
int _min=rmq2(i,j,i+mid-1,j+mid-1);
if(_max-_min<=g) ans=max(ans,mid),l=mid+1;
else r=mid-1;
}
}
printf("%d\n",ans);
return 0;
}
解法二:因为矩阵内的值范围比较小,考虑枚举可行的[L,L+G],并处理出01矩阵,其中1表示当前值能够被选取。
对于预处理出的每个矩阵,考虑dp求解以当前点为矩阵右下角的点时,所能选取的最大长度,即若当前点能够选取,则当前点的能够选取的最大长度为dp[i][j]=min(dp[i-1][j-1],dp[i-1][j],dp[i][j-1])+1.
Ac code:
#include <bits/stdc++.h>
using namespace std;
int dp[501][501],a[501][501];
vector<int>res;
int main()
{
int n,m,g;
scanf("%d%d%d",&n,&m,&g);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&a[i][j]),res.push_back(a[i][j]);
sort(res.begin(),res.end());
res.erase(unique(res.begin(),res.end()),res.end());
int ans=1;
for(int p=0;p<res.size();p++){
int l=res[p],r=res[p]+g;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
dp[i][j]=a[i][j]>=l&&a[i][j]<=r;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
dp[i][j]=dp[i][j]?min(min(dp[i-1][j],dp[i][j-1]),dp[i-1][j-1])+1:0;
ans=max(ans,dp[i][j]);
}
}
printf("%d\n",ans);
return 0;
}
J题:https://ac.nowcoder.com/acm/contest/700/J
题意:给定一个序列S,求子序列的和不能组成的最小值。
分析:对所给序列排序,遍历序列。假设当前可以组成[0,x],加入一个新数Si后可以组成的区间为[0,x]∪[Si,x+Si],其中若x+1<Si,则无法取到x+1的值。
Ac code:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a[100005];
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
sort(a+1,a+n+1);
ll sum=0;
for(int i=1;i<=n;i++)
{
if(a[i]>sum+1) break;
sum+=a[i];
}
printf("%lld\n",sum+1);
return 0;
}