比赛链接
A abc
前缀a的数量,后缀c的数量,遇到b就计算一次答案。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5+100;
char s[N];
int cnt[N];
int main()
{
ll ans= 0 ;
scanf("%s",s+1);
int n = strlen(s+1);
for(int i=n;i>=1;i--){
if(s[i]=='c') cnt[i] = cnt[i+1] +1;
else cnt[i] = cnt[i+1];
}
int tmp =0 ;
for(int i=1;i<=n;++i){
if(s[i]=='a') tmp++;
else if(s[i]=='b') ans+=1ll*tmp*cnt[i];
}
printf("%lld\n",ans);
}
B-子串查询
预处理出每个字符后面26个字母第一次出现的位置。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5+100;
char s[N];
int pos[26][N],cnt[26],cur[N][26];
int main()
{
int n,m;
scanf("%d%d",&n,&m);
scanf("%s",s+1);
for(int i=1;i<=n;i++)
pos[s[i]-'a'][++cnt[s[i]-'a']]=i;
for(int i=0;i<=n;i++){
for(int j=0;j<26;j++){
int t = upper_bound(pos[j]+1,pos[j]+cnt[j]+1,i) - pos[j];
if(t!=cnt[j]+1) cur[i][j]=pos[j][t];
}
}
while(m--){
char t[60];
scanf("%s",t+1);
int now = 0, len = strlen(t+1),flag=1;
for(int i=1;i<=len;i++){
if(cur[now][t[i]-'a']!=0) now = cur[now][t[i]-'a'];
else {
flag=0;
break;
}
}
puts(flag?"YES":"NO");
}
}
C-勾股定理
这题比赛时一脸懵逼。周大佬说是以前打过的一道网络赛原题,好吧,忘记了。搜了一波题解。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
ll n;
cin>>n;
if(n<=2) puts("-1");
else {
ll b,c;
if(n % 2){
b = (n*n-1)/2;
c = (n*n+1)/2;
}else {
b = (n*n/2-2)/2;
c = (n*n/2+2)/2;
}
printf("%lld %lld\n",c,b);
}
}
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
ll n;
cin>>n;
if(n<=2) puts("-1");
else {
ll b,c;
if(n%2==0){
b = (n*n + 4)/4;
c = b-2;
}else{
b = (n*n + 1)/2;
c = b - 1;
}
printf("%lld %lld\n",c,b);
}
}
D-羊吃草
解法一:
比赛时想的是贪心暴力。对于每一个位置记录能被哪些羊儿吃以及羊儿能吃到的左端点,然后对这些左端点进行从小到大排序。每次询问一个区间时,怎样给每个位置i分配羊儿呢?贪心选左端点最近的,假设现在有两个端点L1,L2,L1<L2,对于i到L1,以及i到L2,显然L2覆盖的区间大一些,那么L2或许就可以分配给其它L1到不了的位置,所以贪心选L1,并且标记L1。
#include<bits/stdc++.h>
using namespace std;
const int N = 400+10;
int vis[N];
struct node{
int l,r;
bool friend operator < (node c,node d){
return c.r<d.r;
}
}a[N]; vector<node>v[N];
int main()
{
int n,q;
scanf("%d%d",&n,&q);
for(int i=1;i<=n;++i)
scanf("%d",&a[i].l);
for(int i=1;i<=n;++i)
scanf("%d",&a[i].r);
for(int i=1;i<=400;i++){
for(int j=1;j<=n;j++){
if(a[j].l<=i&&a[j].r>=i){
v[i].push_back(node{j,a[j].r});
}
}
}
for(int i=1;i<=400;++i){
sort(v[i].begin(),v[i].end());
}
while(q--){
int l,r,ans=0;
scanf("%d%d",&l,&r);
for(int i=1;i<=400;i++) vis[i]=0;
for(int i=l;i<=r;++i){
for(auto t:v[i]){
if(!vis[t.l]){
vis[t.l]=1;
ans++;
break;
}
}
}
printf("%d\n",ans);
}
}
解法二:
其实这就是一个二分图,对于羊儿能到达区间的每一个点建一条边,然后在询问区间内跑一遍匈牙利就行了。
#include<bits/stdc++.h>
using namespace std;
vector<int>e[1000];
int vis[1000],match[1000],a[1000],b[1000];
int n,q;
int dfs(int x)
{
for(int i=0;i<e[x].size();i++)
{
int y=e[x][i];
if(!vis[y]){
vis[y]=1;
if(match[y]==0||dfs(match[y]))
{
match[y]=x;
return 1;
}
}
}
return 0;
}
int main()
{
scanf("%d%d",&n,&q);
for(int i=1;i<=n;++i)
scanf("%d",&a[i]);
for(int i=1;i<=n;i++){
scanf("%d",&b[i]);
for(int j=a[i];j<=b[i];j++){
e[j].push_back(i);
}
}
while(q--){
int ans=0,l,r;
scanf("%d%d",&l,&r);
memset(match,0,sizeof match);
for(int i=l;i<=r;i++)
{
memset(vis,0,sizeof(vis));
if(dfs(i)) ans++;
}
printf("%d\n",ans);
}
}
E-数列
比赛时想到了一定会是一个大的循环节,但是对于剩下的部分位置不知道咋分配。。。
定义 1 2 3 ... k 为一段。那么我们就是要在n个位置构造sum段这样的1~k。
能得到的答案就是n - sum。所以sum越小,答案就越大。
设这sum段中长度最小的段长度为mn,长度最大的段长度为mx,可以发现如果我们已知了sum,那么要使这sum段的总和最小,就要使得mn和mx尽可能的接近,因为如果mx-mn>=2,那么段mx的最后一个元素的值为mx,段mn最后一个元素的值为mn,那么我们使段mx的长度-1,mn的长度加1。那么总和的变化就是总和=总和-mx+mn+1。因为mx-mn>=2,所以mx>mn+1。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m,mx=1e9;
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
if(1ll*i*(1+n/i)*(n/i)/2+1ll*(n%i)*(n/i+1)<=m) mx = min(mx,i);
for(int i=1;i<=n%mx;i++){
for(int j=1;j<=n/mx+1;j++)
printf("%d ",j);
}
for(int i=1;i<=mx-n%mx;i++){
for(int j=1;j<=n/mx;j++)
printf("%d ",j);
}
puts("");
}