26.3.18 1600-1800 板刷日记
B. Shortest Statement Ever rating:1800
tag:位运算,贪心,guess
感觉 \(x,y\) 是可以至少有一个不变的,枚举不变的那个,再尝试对另一个变化,去找最小幅度变大或变小的值
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
const ll inf = 1e18;
const int mod = 998244353;
void solve(){
int x,y;
cin>>x>>y;
if((x&y)==0){
cout<<x<<" "<<y<<endl;
return;
}
vector<int> cnt(40);
for(int i=31;i>=0;i--){
cnt[i]+=(x>>i & 1);
cnt[i]+=(y>>i & 1);
}
auto f=[&](int a,int b)-> vector<int> {
int p=a,q=b;
int pos=-1;
for(int i=31;i>=0;i--){
if(cnt[i]==2){
if(pos==-1){
pos=i;
}
q^=(1<<i);
}
}
for(int i=pos-1;i>=0;i--){
if(cnt[i]==0){
q^=(1<<i);
}
}
int ans1=q,ans2=q;
q=b;
for(int i=pos+1;i<=30;i++){
if(cnt[i]==0){
q^=(1<<i);
for(int j=i-1;j>=0;j--){
if(q>>j & 1) q^=(1<<j);
}
ans1=q;
break;
}
}
return {ans1,ans2};
};
int diff=inf;
pii ans;
for(auto val:f(x,y)){
if(abs(y-val)<diff){
ans={x,val};
diff=abs(y-val);
}
}
for(auto val:f(y,x)){
if(abs(x-val)<diff){
ans={val,y};
diff=abs(x-val);
}
}
cout<<ans.first<<" "<<ans.second<<endl;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
int ct=1;
cin>>ct;
while(ct--){
solve();
}
}
G. Mixing MEXes rating:1800
tag:模拟,MEX
先处理出不变时的全局的答案,然后去枚举选择的数字
如果这个数字对某个数组会造成影响(相当于这个数是其他数组的 \(mex\)),则将这个数组 \(mex\) 的变化修改进预处理的全局答案中。
然后再看这个拿走这个数字后,会不会对原数组本身造成影响,维护这个变化
可以用 \(map\) 处理并记录下每个数组第一个 \(mex\) 和第二个 \(mex\)
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
const ll inf = 1e18;
const int mod = 998244353;
vector<int> d(1e6+5);
void solve(){
int n;
cin>>n;
vector<int> l(n+1);
vector<vector<int>> a(n+1);
for(int i=1;i<=n;i++){
cin>>l[i];
a[i].resize(l[i]+1);
for(int j=1;j<=l[i];j++){
cin>>a[i][j];
}
sort(a[i].begin()+1,a[i].end());
}
int sum=0;
vector<pii> tmp;
vector<map<int,int>> cnt(n+1);
vector<int> m1(n+1),m2(n+1);
for(int i=1;i<=n;i++){
auto &mp=cnt[i];
for(int j=1;j<=l[i];j++){
mp[a[i][j]]++;
}
int mex1=-1,mex2=-1;
for(int val=0;;val++){
if(mp[val]==0){
if(mex1==-1) mex1=val;
else{
mex2=val;
break;
}
}
}
tmp.push_back({mex1,mex2-mex1});
d[mex1]+=mex2-mex1;
sum+=mex1;
m1[i]=mex1,m2[i]=mex2;
}
int ans=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=l[i];j++){
int add=sum*(n-1)+d[a[i][j]];
if(m1[i]>a[i][j] && cnt[i][a[i][j]]==1) add+=(a[i][j]-m1[i])*(n-1);
ans+=add;
}
}
cout<<ans<<endl;
for(auto [x,y]:tmp){
d[x]-=y;
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
int ct=1;
cin>>ct;
while(ct--){
solve();
}
}
F. BattleCows rating:1700
tag:模拟,分治思想,二进制,淘汰赛
对每个位置,最多有 \(n\) 场比赛,且每场比赛对手位置一定是固定的,知道 \(b\) 就可以算出来。
每场比赛的对手值都是用一些连续区间的数字异或起来的,且数值固定。
所以可以用前缀异或和数组,单次 \(O(1)\),一共 \(O(n)\) 的查询对手的值即可
主要是代码实现
查询区间异或值用前缀和即可,完全不用线段树,我这是一开始以为要修改,就直接拉了个线段树。但其实完全不用修改
点击查看代码
#include<bits/stdc++.h>
// #define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
const ll inf = 1e18;
const int mod = 998244353;
class SegmentTree{
public:
#define lc u<<1
#define rc u<<1|1
struct Node{
int l,r,sum; // sum 现在表示区间异或和
int add; // add 现在表示异或懒标记
};
int n;
vector<int> w;
vector<Node> tr;
SegmentTree(int n){
init(n);
}
void init(int n){
this->n=n;
w.resize(n+10);
tr.resize(4*n+10);
}
void pushup(int u){
tr[u].sum=tr[rc].sum^tr[lc].sum; // 加法改为异或
}
void pushdown(int u){
if(tr[u].add){
tr[rc].add^=tr[u].add; // 加法改为异或
tr[lc].add^=tr[u].add;
// 如果区间长度为奇数,异或和才需要更新
if((tr[rc].r-tr[rc].l+1)&1) tr[rc].sum^=tr[u].add;
if((tr[lc].r-tr[lc].l+1)&1) tr[lc].sum^=tr[u].add;
tr[u].add=0;
}
return;
}
void build(int u,int l,int r){
if(l==r) tr[u]={l,r,w[r],0};
else{
tr[u]={l,r};
int mid=l+r>>1;
build(lc,l,mid);
build(rc,mid+1,r);
pushup(u);
}
}
int query(int u,int l,int r){
if(l<=tr[u].l && r>=tr[u].r) return tr[u].sum;
else{
pushdown(u);
int sum=0,mid=tr[u].l+tr[u].r>>1; // 0 是异或的单位元
if(l<=mid) sum^=query(lc,l,r); // 加法改为异或
if(r>mid) sum^=query(rc,l,r); // 加法改为异或
return sum;
}
}
void modify(int u,int l,int r,int k){
if(l<=tr[u].l && r>=tr[u].r){
// 如果区间长度为奇数,异或和才需要更新
if((tr[u].r-tr[u].l+1)&1) tr[u].sum^=k;
tr[u].add^=k; // 懒标记叠加改为异或
}
else{
pushdown(u);
int mid=tr[u].r+tr[u].l>>1;
if(l<=mid) modify(lc,l,r,k);
if(r>mid) modify(rc,l,r,k);
pushup(u);
}
}
};
void solve(){// 常数优化版
int n,q;
cin>>n>>q;
int k=n;
n=(1<<n);
vector<int> a(n+1);
SegmentTree tr(n);
for(int i=1;i<=n;i++){
cin>>a[i];
tr.w[i]=a[i];
}
tr.build(1,1,n);
while(q--){
int b,c;
cin>>b>>c;
int ans=0;
int val=c,pos=b;
tr.modify(1,b,b,a[b]^c);
for(int k1=0;k1<k;k1++){
int tar=pos;
if(pos&1) tar++;
else tar--;
int r=tar*(1<<k1);
int l=r-(1<<k1)+1;
int tmp=tr.query(1,l,r);
if(tmp>val){
ans+=(r-l+1);
}
else if(tmp==val){
if((pos&1) == 0) ans+=(r-l+1);
}
val^=tmp;
pos=(pos+1)/2;
}
cout<<ans<<endl;
tr.modify(1,b,b,a[b]^c);
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
int ct=1;
cin>>ct;
while(ct--){
solve();
}
}
E. Exquisite Array rating:1800
tag:计数
按照 \(k\) 从 \(1\) 到 \(n-1\) 的顺序处理,一开始 \(k=1\) 的答案是固定的
维护一个 \(set\),初始值为 \(\{ 1,n \}\)
当 \(k=i\) 时,找到所有 \((a[p-1],a[p])\) 差值为 \(i\) 的 \(p-1,p\)
相当于,在区间上,要减去当前被统计的,横跨 \(p-1,p\) 的区间
找到 \(set\) 中第一个小于等于 \(p-1\) 的位置 \(l\) 和第一个大于等于 \(p\) 的位置 \(r\)
要减去的区间数量即为 \(((p-1)-l+1)*(r-p+1)\)
在纸上画画容易想明白
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
const ll inf = 1e18;
const int mod = 998244353;
void solve(){
int n;
cin>>n;
vector<int> a(n+1);
vector<vector<pii>> d(n+1);
for(int i=1;i<=n;i++){
cin>>a[i];
if(i!=1){
d[abs(a[i]-a[i-1])].push_back({i-1,i});
}
}
set<int> st;
st.insert(1);
st.insert(n);
int ans=(n-1)*n/2;
cout<<ans<<" ";
for(int k=1;k<n-1;k++){
for(auto [x,y]:d[k]){
int r=*st.lower_bound(y);
auto it=st.upper_bound(x);
it--;
int l=*it;
st.insert(x);
st.insert(y);
ans-=(x-l+1)*(r-y+1);
}
cout<<ans<<" ";
}
cout<<endl;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
int ct=1;
cin>>ct;
while(ct--){
solve();
}
}
D. Unfair Game rating:1600
tag:二进制,组合数学
对于任何一个数,把它变成 \(0\) 的操作次数和操作方式都是固定的。
实际上就是找,对于 \(1-n\),有多少个数满足,变成 \(0\) 的操作次数 \(>k\)
转化成二进制,变成 \(0\) 的操作次数等价于,二进制位数 \(+\) 二进制中 \(1\) 的个数 \(-~1\)
\(-~1\) 是因为二进制表示是 \(0\) 时,就已经结束了
枚举二进制位数,推一些组合数即可
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
const ll inf = 1e18;
const int mod = 1e9+7;
const int N=61;
vector<int> fact(N);
void init(){
fact[0]=fact[1]=1;
for(int i=2;i<N;i++){
fact[i]=i*fact[i-1]%mod;
}
}
int qmi(int a,int b,int p){
int res=1;
while(b){
if(b&1) res=res*a%p;
b>>=1;
a=a*a%p;
}
return res;
}
int C(int a,int b){
if(a<b || a<0 || b<0) return 0;
return fact[a]*qmi(fact[b],mod-2,mod)%mod*qmi(fact[a-b],mod-2,mod)%mod;
}
void solve(){
int n,k;
cin>>n>>k;
int d=0;
while((1<<d)!=n) d++;
int ans=0;
for(int i=1;i<=d;i++){
int l=k-i+1,r=i-1;
for(int j=l;j<=r;j++){
ans+=C(i-1,j);
}
}
//特判 n
if(d+1>k) ans++;
cout<<ans<<endl;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
init();
int ct=1;
cin>>ct;
while(ct--){
solve();
}
}
E. New Year's Gifts rating:1800
tag:贪心,认真读题!!!!!!
首先,注意 \(k\) 数值上的限制。
其次,每个礼物都是要买的。也就是说,\(y\) 是固定消耗
尽可能选择 \(z-y\) 最大的使用箱子装起来
剩下的就只能花钱买了
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
const ll inf = 1e18;
const int mod = 998244353;
struct node{
int x,y,z;
bool operator<(node &t){
return z-y>t.z-t.y;
}
};
void solve(){
int n,m,k;
cin>>n>>m>>k;
vector<int> a(m+1);
multiset<int> st;
for(int i=1;i<=m;i++){
cin>>a[i];
st.insert(a[i]);
}
vector<node> p(n+1);
for(int i=1;i<=n;i++){
cin>>p[i].x>>p[i].y>>p[i].z;
k-=p[i].y;
}
sort(p.begin()+1,p.end());
vector<int> cost;
int ans=0;
for(int i=1;i<=n;i++){
auto it=st.lower_bound(p[i].x);
if(it!=st.end()){
ans++;
st.erase(it);
}
else{
cost.push_back(p[i].z-p[i].y);
}
}
sort(cost.begin(),cost.end());
for(auto val:cost){
if(k>=val){
ans++;
k-=val;
}
else break;
}
cout<<ans<<endl;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
int t=1;
cin>>t;
while(t--){
solve();
}
}
E. Blackslex and Girls rating:1800
tag:模拟,贪心,妙妙题,代码长
先判断 \(p\) 的和是否大于 \(x+y\)
如果 \(s[i]='1'\) 则直接给 \(a[i]=p[i],b[i]=0\),反之一样
然后判断 \(a,b\) 是否大于 \(x,y\),并尝试修改(因为判断过总和,所以这里只可能有一个大于)
然后再判断 \(a,b\) 是否小于 \(x,y\),并尝试修改。
注意如果 \(s\) 中有 \(0\) 有 \(1\),则无需判断小于。因为能加回来
如果有一个是小于的,则判断能不能加上。
如果能通过所有判断,就是 YES
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll = long long;
using ull = unsigned long long;
const ll inf = 1e18;
const int mod = 998244353;
void solve(){
int n,x,y;
cin>>n>>x>>y;
vector<int> a(n+1),b(n+1),p(n+1);
string s;
cin>>s;
s=" "+s;
for(int i=1;i<=n;i++){
cin>>p[i];
if(s[i]=='0') a[i]=p[i];
else b[i]=p[i];
}
if(accumulate(p.begin()+1,p.end(),0ll)>x+y){
cout<<"NO\n";
return;
}
if(accumulate(a.begin()+1,a.end(),0ll)>x){
int diff=accumulate(a.begin()+1,a.end(),0ll)-x;
if(accumulate(b.begin()+1,b.end(),0ll)+diff>y){
cout<<"NO\n";
return;
}
for(int i=1;i<=n;i++){
if(s[i]=='0'){
int val=a[i]/2-(a[i]%2==0);
if(val>diff) val=diff;
b[i]=val;
a[i]-=val;
diff-=val;
if(diff==0) break;
}
if(i==n){
cout<<"NO\n";
return;
}
}
}
else if(accumulate(b.begin()+1,b.end(),0ll)>y){
int diff=accumulate(b.begin()+1,b.end(),0ll)-y;
if(accumulate(a.begin()+1,a.end(),0ll)+diff>x){
cout<<"NO\n";
return;
}
for(int i=1;i<=n;i++){
if(s[i]=='1'){
int val=b[i]/2-(b[i]%2==0);
if(val>diff) val=diff;
a[i]=val;
b[i]-=val;
diff-=val;
if(diff==0) break;
}
if(i==n){
cout<<"NO\n";
return;
}
}
}
bool f0=1,f1=1;
for(int i=1;i<=n;i++){
if(s[i]=='1') f0=0;
else f1=0;
}
if(f0 && accumulate(b.begin()+1,b.end(),0ll)<y){
//只考虑 b 是否<y
int diff=y-accumulate(b.begin()+1,b.end(),0ll);
for(int i=1;i<=n;i++){
int val=a[i]-1-b[i];
diff-=val;
b[i]=a[i]-1;
}
diff-=x-accumulate(a.begin()+1,a.end(),0ll);
if(diff>0){
cout<<"NO\n";
return;
}
}
if(f1 && accumulate(a.begin()+1,a.end(),0ll)<x){
//只考虑 a 是否<x
int diff=x-accumulate(a.begin()+1,a.end(),0ll);
for(int i=1;i<=n;i++){
int val=b[i]-1-a[i];
diff-=val;
a[i]=b[i]-1;
}
diff-=y-accumulate(b.begin()+1,b.end(),0ll);
if(diff>0){
cout<<"NO\n";
return;
}
}
cout<<"YES\n";
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
int ct=1;
cin>>ct;
while(ct--){
solve();
}
}

浙公网安备 33010602011771号