比赛链接
#include
using namespace std;
int main() {
int t;
scanf("%d",&t);
while(t--) {
int n,x,a,b;
scanf("%d %d %d %d",&n,&x,&a,&b);
printf("%d\n",min(n-1,abs(a-b)+x));
}
}
#include
using namespace std;
bool ok(int x,int y) {
if(x==1) return y<=1;
else if(x<=3) return y<=3;
else return 1;
}
int main() {
int t,x,y;scanf("%d",&t);
while(t--) {
scanf("%d %d",&x,&y);
printf(ok(x,y)?"YES\n":"NO\n");
}
}
就是给你一个数组,让你找出一个最短的子段,是的子段内元素出现的最多次数对应的元素唯一,比如 [ 1 , 1 , 2 , 2 , 2 ] [1,1,2,2,2] [1,1,2,2,2]就可以,而 [ 1 , 1 , 2 , 2 ] [1,1,2,2] [1,1,2,2]就不可以
贪心,计算每两个相同元素的距离对 a n s ans ans取 m i n min min
#include
using namespace std;
const int maxn=2e5+10;
int n,a[maxn],pre[maxn];
int main() {
int t;
scanf("%d",&t);
while(t--) {
scanf("%d",&n);
int ans=0x3f3f3f3f;
for(int i=1;i<=n;i++) pre[i]=-1;
for(int i=1;i<=n;i++) {
scanf("%d",&a[i]);
if(pre[a[i]]!=-1) ans=min(ans,i-pre[a[i]]+1);
pre[a[i]]=i;
}
printf("%d\n",ans==0x3f3f3f3f?-1:ans);
}
}
就是说有有 n n n个怪物, m m m个英雄,怪物排好队从 1 − n 1-n 1−n排好队接受因胸的挑战,每个怪物有一个 p o w e r power power值 a i a_i ai,每个英雄有两个属性值: p o w e r power power值 p i p_i pi和忍耐值 s i s_i si。每个回合你需要派一个英雄取战斗。设这 n n n个怪物已经被消灭了 ( k − 1 ) (k-1) (k−1)个,当前战斗的是第 k k k个。如果英雄的 p o w e r power power值严格小于第 k k k个怪物的power值 或者本次回合英雄打败的怪物数量等于它的忍耐值 s i s_i si 或者 所有怪物都已经被消灭,他会结束这一回合;否则继续与第 k + 1 k+1 k+1个怪物战斗。问最少的回合消灭所有怪物,无解输出 − 1 -1 −1。
贪心,对于每一个忍耐值,当然选择对应的最大 p o w e r power power值的英雄战斗,令 b [ i ] b[i] b[i]表示忍耐值为 i i i的所有英雄中最大的 p o w e r power power值,然后维护 b b b数组的后缀最大值 s u f [ i ] suf[i] suf[i],然后指针从左往右扫 a a a数组,如果能选择当前怪物就选,判断方法就是大于当前区间长度的忍耐值的最大 p o w e r power power值 s u f [ l e n ] suf[len] suf[len]如果大于区间最大 p o w e r power power值,则可以选
#include
using namespace std;
const int maxn=2e5+10;
int n,m,a[maxn],suf[maxn],b[maxn],p[maxn],s[maxn];
int main() {
int t;scanf("%d",&t);
while(t--) {
scanf("%d",&n);
for(int i=1;i<=n;i++) b[i]=-1;
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
scanf("%d",&m);
for(int i=1;i<=m;i++) scanf("%d %d",&p[i],&s[i]),b[s[i]]=max(b[s[i]],p[i]);
suf[n+1]=-1;
for(int i=n;i>=1;i--) suf[i]=max(suf[i+1],b[i]);
int pos=1,ans=0;
while(pos<=n) {
int cur=pos;
int maxx=a[pos];
while(cur<=n && suf[cur-pos+1]>=maxx) {
cur++;
maxx=max(maxx,a[cur]);
}
if(cur==pos) {ans=-1;break;}
ans++;
pos=cur;
}
printf("%d\n",ans);
}
}
就是说有 n n n个数 ( 1 , 2 , 3... n ) (1,2,3...n) (1,2,3...n)分给了 a , b , c a,b,c a,b,c三个同学,其中 a a a有 k 1 k_1 k1个, b b b有 k 2 k_2 k2个, c c c有 k 3 k_3 k3个,保证这三个集合的并集是一个长度为 n n n的全排列,现在需要最少的操作使得 a a a手中的所有数是 1 , 2 , 3... n 1,2,3...n 1,2,3...n的一个前缀, c c c手中的所有数是 1 , 2 , 3... n 1,2,3...n 1,2,3...n的一个后缀,其余的在 b b b手中,每次操作可以让一个人给一个自己手中的数给另外一个人
考虑枚举 a a a最后手中的前缀的长度,然后怎样让操作最少使得 c c c成为一个后缀呢?首先声明两个数组的含义:
那么显然最小操作数就等于 m i n ( p e w [ i ] + s u f [ i + 1 ] ) i ∈ [ 0 , n ] min(pew[i]+suf[i+1]) i\in [0,n] min(pew[i]+suf[i+1])i∈[0,n],然后有个问题就是对于你枚举的 a a a的前缀长度 L L L, [ L + 1 , n ] [L+1,n] [L+1,n]中的每一个数不一定都在 b b b或者 c c c手中,而且 [ 1 , L ] [1,L] [1,L]中的每一个数也不一定都在 a a a手中,如果不在 a a a手中,那么一定需要把 b b b和 c c c手中的所有在区间 [ 1 , L ] [1,L] [1,L]内的数都换到 a a a手中,同样 a a a手中的在区间 [ L + 1 , n ] [L+1,n] [L+1,n]的数也都要换到 b b b或者 c c c手中,这里具体换到 b b b还是 c c c取决于 c c c对应的最优后缀长度 R R R,如果 a a a中的某个数在区间 [ n − R + 1 , n ] [n-R+1,n] [n−R+1,n]中,则给 c c c,否则给 b b b,花费都是 1 1 1,所以我们不用关心他会给谁,总花费一定是 a a a手中的在区间 [ L + 1 , n ] [L+1,n] [L+1,n]中的个数,那么这样看来当你从 b b b或者 c c c中取走一个数时, p r e [ i ] pre[i] pre[i]和 s u f [ i ] suf[i] suf[i]是在变化的,考虑直接用线段树维护 p r e [ i ] + s u f [ i + 1 ] pre[i]+suf[i+1] pre[i]+suf[i+1]的最小值,那么当从 b b b中取走一个数 v v v到 a a a时,区间 [ 0 , v − 1 ] [0,v-1] [0,v−1]的值就要减一,如果从 c c c中取走一个数 v v v到 a a a,那么区间 [ v , n ] [v,n] [v,n]的值减一,并且维护当前取到 a a a中的总个数 t o t tot tot,然后当前答案就是 t o t + ( ∑ j = i + 1 n j ∈ a ) + q u e r y _ m i n ( i , n ) tot+(\sum_{j=i+1}^{n}{j\in a})+query\_min(i,n) tot+(∑j=i+1nj∈a)+query_min(i,n)其中 i i i是当前枚举的前缀长度,注意这里是查询 [ i , n ] [i,n] [i,n]的最小值而不是 [ 0 , n ] [0,n] [0,n]的最小值,赛场上就是因为这个小细节被搞自闭了。
#include
using namespace std;
const int maxn=2e5+10;
int n,k1,k2,k3,a[maxn],b[maxn],c[maxn],pre[maxn],suf[maxn],ea[maxn],eb[maxn],ec[maxn],cnt[maxn],sufa[maxn];
namespace segment_tree{
int mark[maxn<<2],minn[maxn<<2],maxx[maxn];
void pushup(int id) {
minn[id]=min(minn[id<<1],minn[id<<1|1]);
}
void down(int id) {
mark[id<<1]+=mark[id];mark[id<<1|1]+=mark[id];
minn[id<<1]+=mark[id];minn[id<<1|1]+=mark[id];
mark[id]=0;
}
void build(int id,int l,int r) {
mark[id]=minn[id]=0;
if(l==r) {minn[id]=cnt[l];return;}
int mid=(l+r)>>1;
build(id<<1,l,mid);build(id<<1|1,mid+1,r);
pushup(id);
}
void update(int id,int L,int R,int l,int r,int add) {
if(l<=L&&R<=r) {
mark[id]+=add;minn[id]+=add;
return;
}
if(mark[id]!=0) down(id);int mid=(L+R)>>1;
if(l<=mid) update(id<<1,L,mid,l,r,add);
if(r>mid) update(id<<1|1,mid+1,R,l,r,add);
pushup(id);
}
int query_max(int id,int L,int R,int l,int r) {
if(l<=L&&R<=r) return maxx[id];int res=-0x3f3f3f3f;
if(mark[id]!=0) down(id);int mid=(L+R)>>1;
if(l<=mid) res=max(res,query_max(id<<1,L,mid,l,r));
if(r>mid) res=max(res,query_max(id<<1|1,mid+1,R,l,r));
return res;
}
int query_min(int id,int L,int R,int l,int r) {
if(l<=L&&R<=r) return minn[id];int res=0x3f3f3f3f;
if(mark[id]!=0) down(id);int mid=(L+R)>>1;
if(l<=mid) res=min(res,query_min(id<<1,L,mid,l,r));
if(r>mid) res=min(res,query_min(id<<1|1,mid+1,R,l,r));
return res;
}
}
using namespace segment_tree;
int main() {
scanf("%d %d %d",&k1,&k2,&k3);
n=k1+k2+k3;
for(int i=1;i<=k1;i++) scanf("%d",&a[i]),ea[a[i]]=1;
for(int i=1;i<=k2;i++) scanf("%d",&b[i]),eb[b[i]]=1;
for(int i=1;i<=k3;i++) scanf("%d",&c[i]),ec[c[i]]=1;
for(int i=1;i<=n;i++) pre[i]=pre[i-1]+ec[i];
for(int i=n;i>=1;i--) suf[i]=suf[i+1]+eb[i],sufa[i]=sufa[i+1]+ea[i];
for(int i=0;i<=n;i++) cnt[i]=pre[i]+suf[i+1];
build(1,0,n);
int ans=0x3f3f3f3f;
for(int i=0,tot=0;i<=n;i++) { //tot表示转入多少到第一个人
ans=min(ans,tot+sufa[i+1]+query_min(1,0,n,i,n));
if(eb[i+1]) tot++,update(1,0,n,0,max(i-1,0),-1);
else if(ec[i+1]) tot++,update(1,0,n,i,n,-1);
}
printf("%d\n",ans);
}
就是给你 100 100 100个在区间 [ 0 , 2 30 − 1 ] [0,2^{30}-1] [0,230−1]的数,让你找一个数 x x x,将所有数都异或上 x x x后,所有数比特位为 1 1 1的数量都相同
感觉还是很容易想到答案的。只是因为太菜 E E E题被搞没时间看这个题了。考虑 m e e t i n t h e m i d d l e meet\ in\ the\ middle meet in the middle即将 30 30 30个 b i t bit bit为一分为二,然后两边暴力枚举 x x x的所有状态然后异或上这 100 100 100个数,把异或后的状态存下来,然后考虑两边是否存在一种组合情况使得加起来都相等,对于怎么找出一组合法的解这里有一个技巧就是,把所有数减去数组内最小值,另外一半先用 15 15 15减去所有值,然后再都减去最小值,如果这两个数组相同,则可以。对于怎么判断数组相同,可以直接 m a p < v e c t o r < i n t > , i n t > map
#include
using namespace std;
const int maxn=105;
map<vector<int>,int>mp;
int n,a[maxn];
int main() {
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=0;i<(1<<15);i++) {
vector<int> vec;
for(int j=1;j<=n;j++) vec.push_back(__builtin_popcount((a[j]>>15) ^ i ));
int minn=*min_element(vec.begin(),vec.end());
for(int j=0;j<vec.size();j++) vec[j]-=minn;
mp[vec]=i;
}
int all=(1<<15)-1;
for(int i=0;i<(1<<15);i++) {
vector<int>vec;
for(int j=1;j<=n;j++) vec.push_back(15-__builtin_popcount((a[j] & all) ^ i));
int minn=*min_element(vec.begin(),vec.end());
for(int j=0;j<vec.size();j++) vec[j]-=minn;
if(mp.count(vec)) {
int left=mp[vec];
return printf("%d\n",left<<15|i),0;
}
}
printf("-1\n");
}