A. Ropewalkers
分析:固定中间点,移动两边的点就可以了。
#include "bits/stdc++.h"
using namespace std;
const int mod = 1e9+7;
long long qk(long long a,long long n){ long long ans = 1;while(n){ if(n&1)ans = ans * a %mod;n>>=1;a=a*a%mod; }return ans; }
struct BIT {
int n;vector<int> v;
BIT(int n) : n(n) { v.resize(n + 1); }
void update(long long x, long long d) { while (x <= n) { v[x] += d;/*if (v[x] >= mod)v[x] -= mod*/;x += (x & -x); }}
long long que(long long x) { long long res = 0;while (x > 0) { res += v[x];/*if (res >= mod)res -= mod*/;x -= (x & -x); }return res; }
};
int n;
long long a[10];
int main(){
long long d;
cin>>a[0]>>a[1]>>a[2]>>d;
sort(a,a+3);
long long ans = 0;
if(a[1]-a[0]<d)ans += d - (a[1]-a[0]);
if(a[2]-a[1]<d)ans += d - (a[2]-a[1]);
cout<<ans<<endl;
}
B. Email from Polycarp
分析:双下标跑一遍就可以了。
#include "bits/stdc++.h"
using namespace std;
const int mod = 1e9+7;
long long qk(long long a,long long n){ long long ans = 1;while(n){ if(n&1)ans = ans * a %mod;n>>=1;a=a*a%mod; }return ans; }
struct BIT {
int n;vector<int> v;
BIT(int n) : n(n) { v.resize(n + 1); }
void update(long long x, long long d) { while (x <= n) { v[x] += d;/*if (v[x] >= mod)v[x] -= mod*/;x += (x & -x); }}
long long que(long long x) { long long res = 0;while (x > 0) { res += v[x];/*if (res >= mod)res -= mod*/;x -= (x & -x); }return res; }
};
int n;
long long a[10];
int main(){
cin>>n;
while(n--){
string s,t;
cin>>s>>t;
int pos = 0;
bool ok = 1;
for (int i = 0; i < t.length(); ++i) {
if(t[i]==s[pos]){
pos++;
}
else
{
if(i>0&&t[i]==t[i-1])continue;
else ok=0;
}
}
if(pos != s.length())ok=0;
if(ok)puts("YES");
else puts("NO");
}
}
C1. Exam in BerSU (easy version)&&C2. Exam in BerSU (hard version)
分析:维护一个sum代表到第i个位置所有的时间总和,然后在其由大到小的排列中,找出一个最小前缀和使得sum - m <= pre
直接用树状数组维护,插入的时候在t[i]位置add t[i],然后二分找前缀和,由于这个位置可能加了多次,特殊判断一下就可以了。
然而这题t[i]只有100,所以直接计数排序,然后查询的时候遍历也是可以的。(提前打的bit板子果然用上了)
#include "bits/stdc++.h"
using namespace std;
const int mod = 1e9+7;
long long qk(long long a,long long n){ long long ans = 1;while(n){ if(n&1)ans = ans * a %mod;n>>=1;a=a*a%mod; }return ans; }
struct BIT {
int n;vector<int> v;
BIT(int n) : n(n) { v.resize(n + 1); }
void update(long long x, long long d) { while (x <= n) { v[x] += d;/*if (v[x] >= mod)v[x] -= mod*/;x += (x & -x); }}
long long que(long long x) { long long res = 0;while (x > 0) { res += v[x];/*if (res >= mod)res -= mod*/;x -= (x & -x); }return res; }
};
BIT c(100),cc(100);
int n,m;
int t[200004];
int times[104];
multiset<int>s;
int main(){
cin>>n>>m;
for (int i = 1; i <= n; ++i) {
scanf("%d",&t[i]);
}
int sum=0;
memset(times,0, sizeof(times));
for (int i = 1; i <= n; ++i) {
sum+=t[i];
if(sum>m){
int left = sum - m;
int l = 1,r = 100;
int res = 0,ans = 0;
while(l<=r){
int mid = (l+r)>>1;
if(c.que(100)-c.que(mid-1)>=left){
res = mid;
l=mid+1;
ans = cc.que(100)-cc.que(mid-1);
}
else r= mid-1;
}
int duo = c.que(100)-c.que(res-1) - left;
int nu = duo/res;
if(times[res]>nu)ans-=nu;
printf("%d ",ans);
}
else printf("0 ");
c.update(t[i],t[i]);
cc.update(t[i],1);
times[t[i]]++;
}
}
D. Extra Element
分析:排序之后差分得到数组b[i],用map记录一下b[i]出现的次数。
当删除第i个数的时候,要维护新的差分数组,只需要将这个数的两个差分值加起来(因为事先排序了)然后再然进去,所以可以直接枚举删除的点,然后用map维护一下,如果某次删除后map中只有一个元素,那么就是答案了。(当map值减到0时要erase掉)
#include "bits/stdc++.h"
using namespace std;
const int mod = 1e9+7;
long long qk(long long a,long long n){ long long ans = 1;while(n){ if(n&1)ans = ans * a %mod;n>>=1;a=a*a%mod; }return ans; }
struct BIT {
int n;vector<int> v;
BIT(int n) : n(n) { v.resize(n + 1); }
void update(long long x, long long d) { while (x <= n) { v[x] += d;/*if (v[x] >= mod)v[x] -= mod*/;x += (x & -x); }}
long long que(long long x) { long long res = 0;while (x > 0) { res += v[x];/*if (res >= mod)res -= mod*/;x -= (x & -x); }return res; }
};
BIT c(100),cc(100);
int n,m;
struct node
{
int w,id;
bool friend operator < (node a,node b){
return a.w<b.w;
}
}a[200004];
int b[200004];
int main(){
cin>>n;
for (int i = 1; i <= n; ++i) {
scanf("%d",&a[i].w);
a[i].id=i;
}
if(n==2||n==3){
printf("1\n");
return 0;
}
sort(a+1,a+1+n);
for (int i = 2; i <= n; ++i) {
b[i]=a[i].w-a[i-1].w;
}
unordered_map<int,int>mp;
int d=-1;
for (int i = 2; i <= n; ++i) {
mp[b[i]]++;
}
if(mp.size()>3){
puts("-1");
return 0;
}
int ans = -1;
for (int i = 1; i <= n; ++i) {
if(i==1){
mp[b[i+1]]--;
if(mp[b[i+1]]==0)mp.erase(b[i+1]);
if(mp.size()==1){
ans = i;
break;
}
mp[b[i+1]]++;
}
else if(i==n){
mp[b[i]]--;
if(mp[b[i]]==0)mp.erase(b[i]);
if(mp.size()==1){
ans = i;
break;
}
mp[b[i]]++;
}
else
{
mp[b[i+1]]--;
mp[b[i]]--;
if(mp[b[i]]==0)mp.erase(b[i]);
if(mp[b[i+1]]==0)mp.erase(b[i+1]);
mp[b[i]+b[i+1]]++;
if(mp.size()==1){
ans = i;
break;
}
mp[b[i+1]]++;
mp[b[i]]++;
mp[b[i]+b[i+1]]--;
if(mp[b[i]+b[i+1]]==0)mp.erase(b[i]+b[i+1]);
}
}
if(ans==-1)puts("-1");
else
cout<<a[ans].id<<endl;
}
E. Polycarp and snakes
分析:比赛时我以为蛇可以是折线。。怀疑人生。
因为蛇只能是直线,所以直接找对应字母第一次出现的地方和最后一次出现的地方,然后跑一个直线就可以了。
注意如果在途中遇见了‘.’或者比自己小的字母,就是NO,或者这条路径没有完全包含所有的该字母,也是NO。
#include "bits/stdc++.h"
using namespace std;
const int mod = 1e9+7;
long long qk(long long a,long long n){ long long ans = 1;while(n){ if(n&1)ans = ans * a %mod;n>>=1;a=a*a%mod; }return ans; }
struct BIT {
int n;vector<int> v;
BIT(int n) : n(n) { v.resize(n + 1); }
void update(long long x, long long d) { while (x <= n) { v[x] += d;/*if (v[x] >= mod)v[x] -= mod*/;x += (x & -x); }}
long long que(long long x) { long long res = 0;while (x > 0) { res += v[x];/*if (res >= mod)res -= mod*/;x -= (x & -x); }return res; }
};
int n,m;
char a[2004][2004];
int ans[30][2][2];
int num[30];
struct node
{
int x,y;
};
int main(){
int t;
cin>>t;
while(t--){
cin>>n>>m;
for (int i = 1; i <= n; ++i) {
scanf("%s",a[i]+1);
}
int maxn = 0;
memset(ans,0, sizeof(ans));
memset(num,0, sizeof(num));
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
if(a[i][j]<='z' && a[i][j]>='a'){
num[a[i][j]-'a']++;
if(ans[a[i][j]-'a'+1][0][0]==0){
ans[a[i][j]-'a'+1][0][0]=i;
ans[a[i][j]-'a'+1][0][1]=j;
}
ans[a[i][j]-'a'+1][1][0]=i;
ans[a[i][j]-'a'+1][1][1]=j;
maxn = max(maxn,a[i][j]-'a'+1);
}
}
}
if(maxn==0){
puts("YES");
puts("0");
continue;
}
bool ok = 1;
for (int i = 1; i <= maxn && ok; ++i) {
if(ans[i][0][0]==0){
ans[i][0][0]=ans[maxn][0][0];
ans[i][0][1]=ans[maxn][0][1];
ans[i][1][0]=ans[maxn][0][0];
ans[i][1][1]=ans[maxn][0][1];
continue;
}
if(ans[i][0][0]!=ans[i][1][0] && ans[i][0][1]!=ans[i][1][1])ok=0;
else
{
int cnt = 0;
if(ans[i][0][0]==ans[i][1][0]){
for (int j = ans[i][0][1]; j <= ans[i][1][1] && ok; ++j) {
if(a[ans[i][0][0]][j]=='.' || a[ans[i][0][0]][j]<i+'a'-1)ok=0;
if(a[ans[i][0][0]][j]==i+'a'-1)cnt++;
}
}else {
for (int j = ans[i][0][0]; j <= ans[i][1][0] && ok; ++j) {
if(a[j][ans[i][0][1]]=='.' || a[j][ans[i][0][1]]<i+'a'-1)ok=0;
if(a[j][ans[i][0][1]]==i+'a'-1)cnt++;
}
}
if(cnt != num[i-1])ok=0;
}
}
if(ok){
puts("YES");
printf("%d\n",maxn);
for (int i = 1; i <= maxn; ++i) {
printf("%d %d %d %d\n",ans[i][0][0],ans[i][0][1],ans[i][1][0],ans[i][1][1]);
}
}
else puts("NO");
}
}
F. Two Pizzas
分析:因为E怀疑人生之后就直接去看剩下过的最多的G1了,赛后发现这题还是比较简单的?
虽然给的数据有1e5个,但是对应的状态只有2^9个,记录每个状态对应的pizza的最小值,然后暴力跑一个两个pizza的组合就可以找出每个状态所需的最小花费。然后枚举512个状态,每种状态对应每个人,如果可以满足这个人,有sra & p == p,sta代表枚举的状态,p代表这个人的需求,所以直接暴力跑就可以了 。
#include "bits/stdc++.h"
using namespace std;
int f[100004];
int like[100004];
long long c[100004];
int r[100004];
int pz[100004];
struct node
{
long long c;
int id;
bool friend operator < (node a,node b){
if(a.c==b.c)return a.id<b.id;
return a.c<b.c;
}
};
multiset<node>a[1120];
long long b[1120];
int posb[1120][2];
int main(){
int n,m;
memset(like,0, sizeof(like));
for (int i = 0; i < (1<<9); ++i) {
b[i]=8e18;
}
cin>>n>>m;
for (int i = 1; i <= n; ++i) {
scanf("%d",&f[i]);
for (int j = 0; j < f[i]; ++j) {
int x;scanf("%d",&x);
x--;
like[i] |= (1<<x);
}
}
for (int i = 1; i <= m; ++i) {
scanf("%lld%d",&c[i],&r[i]);
for (int j = 0; j < r[i]; ++j) {
int x;scanf("%d",&x);
x--;
pz[i] |= (1<<x);
}
a[pz[i]].insert({c[i],i});
}
for (int i = 0; i < (1<<9); ++i) {
for (int j = i; j < (1<<9); ++j) {
if(j==i && a[i].size()>=2){
int cost = a[i].begin()->c + (++a[i].begin())->c;
if(cost < b[i]){
b[i]=cost;
posb[i][0]=a[i].begin()->id;
posb[i][1]=(++a[i].begin())->id;
}
}
else if(j!=i){
if(!a[i].size() || !a[j].size())continue;
int cost = a[i].begin()->c + a[j].begin()->c;
if(cost < b[j|i]){
b[i|j] = cost ;
posb[i|j][0]=a[i].begin()->id;
posb[i|j][1]=a[j].begin()->id;
}
}
}
}
int maxi = 0;
long long mini = 8e18;
int ans=-1;
for (int bit = 0; bit < (1<<9); ++bit) {
int cnt = 0;
for (int i = 1; i <= n; ++i) {
int temp = bit & like[i];
if(temp == like[i])cnt++;
}
if(cnt > maxi && b[bit]!=8e18){
ans = bit;
maxi = cnt;
mini = b[bit];
}
else if(cnt == maxi && mini > b[bit]){
ans = bit;
maxi = cnt;
mini = b[bit];
}
}
if(ans==-1)printf("1 2\n");
else
// printf("%d %d \n",maxi,mini);
printf("%d %d\n",posb[ans][0],posb[ans][1]);
}
G1. Playlist for Polycarp (easy version)
分析:这题其实感觉我是卡过去的。。。
对15首歌曲进行状压,DP[i][j][k]代表时间总长为i,以第j种歌曲结尾,k表示选取状态的方案数。
初始化
dp[0][1][0]=1;
dp[0][2][0]=1;
dp[0][3][0]=1;
因为在放第一首的时候,如果这首歌的种类是1,那么这个dp方程是算了以2,3结尾的,所以这里算了两次,最后答案加起来除个2就可以了。
然后这里dp数组稍微开大一点就MLE了。。果然是玄学过题
#include "bits/stdc++.h"
using namespace std;
const int mod = 1e9+7;
long long qk(long long a,long long n){ long long ans = 1;while(n){ if(n&1)ans = ans * a %mod;n>>=1;a=a*a%mod; }return ans; }
int len[16],g[16];
int dp[226][4][1<<15+1];
int main(){
int n,T;
cin>>n>>T;
for (int i = 0; i < n; ++i) {
scanf("%d%d",&len[i],&g[i]);
}
memset(dp,0, sizeof(dp));
dp[0][3][0]=1;
dp[0][1][0]=1;
dp[0][2][0]=1;
for (int i = 0; i < T; ++i) {
for (int j = 0; j < (1<<n); ++j) {
for (int k = 0; k < n; ++k) {
if(i+len[k]>T)continue;
int x = (1<<k);
if(j&x)continue;
for (int l = 1; l <= 3; ++l) {
if(dp[i][l][j]==0)continue;
if(l==g[k])continue;
dp[i+len[k]][g[k]][j|x]+=dp[i][l][j];
dp[i+len[k]][g[k]][j|x]%=mod;
}
}
}
}
long long ans = 0;
for (int i = 1; i <= 3; ++i) {
for (int j = 0; j < (1<<n); ++j) {
ans += dp[T][i][j];
ans %= mod;
}
}
cout<<ans*qk(2,mod-2)%mod<<endl;
}
G2. Playlist for Polycarp (hard version)
更好的解法还没想到,留坑。