winter week3 day1
2024牛客寒假算法基础集训营2
ATokitsukaze and Bracelet
#include<bits/stdc++.h> using namespace std; #define int long long //#define int __int128 #define double long double typedef pair<int,int>PII; typedef pair<string,int>PSI; typedef pair<string,string>PSS; const int N=100+5,INF=0x3f3f3f3f,Mod=1e9+7,mod=998244353; const int MAXN=1e8+5; const double eps=1e-12; const int dx[4]={-1,1,0,0}; const int dy[4]={0,0,-1,1}; void solve() { int a,b,c; cin>>a>>b>>c; int ans=0; if(a==150)ans++; else if(a==200)ans+=2; if(b>=34&&b<=40)ans+=1; else if(b==45)ans+=2; if(c>=34&&c<=40)ans+=1; else if(c==45)ans+=2; cout<<ans<<'\n'; } signed main(){ ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); int t=1; cin>>t; while(t--){ solve(); } return 0; }
B Tokitsukaze and Cats
思路:枚举,看猫的4个方向有多少没有围栏
#include<bits/stdc++.h> using namespace std; #define int long long //#define int __int128 #define double long double typedef pair<int,int>PII; typedef pair<string,int>PSI; typedef pair<string,string>PSS; const int N=100+5,INF=0x3f3f3f3f,Mod=1e9+7,mod=998244353; const int MAXN=1e8+5; const double eps=1e-12; const int dx[4]={-1,1,0,0}; const int dy[4]={0,0,-1,1}; void solve() { int n,m,k; cin>>n>>m>>k; vector<vector<int>>ve(n+2,vector<int>(m+2)); // vector<PII>g(k); int ans=0; for(int i=0;i<k;++i){ int u,v; cin>>u>>v; ve[u][v]=1; int c=0; for(int j=0;j<4;++j){ int x=u+dx[j],y=v+dy[j]; if(ve[x][y]==0)c++; } ans+=c; } cout<<ans; } signed main(){ ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); int t=1; // cin>>t; while(t--){ solve(); } return 0; }
CTokitsukaze and Min-Max XOR
思路:求的是子序列的贡献,那顺序就不限制,并且有关最大最小值的,首先把序列排序。
这里还是常用的方法,看最大最小值的贡献,若最小值最大值为a[i]和a[j],那这对数的贡献为2j-i-1
若要枚举ij,复杂度n2的显然不行,这里考虑枚举j,然后logn的找到i满足a[i]^a[j]<=k,显然可以用字典树存二进制
对于查找,首先枚举a[j]和k的同位二进制(从高位开始)。若k当前位为1,那么a[i]和a[j]的当前位可以相同或不同,如果相同:说明异或后当前位为0,那么该位后面可以为任意情况,这个时候直接统计子树的值;如果不同:那继续往下一位找,若已经到最后一位了(即叶子节点)直接统计叶子节点的值。若k当前位为0,说明a[i]和a[j]的当前位必须要相同,同样的继续往下一位找
对于每个j的贡献为2j-i-1,可以看作2j-1/2i,那么字典树存的值其实可以为1/2i,这里用逆元求就好了
#include<bits/stdc++.h> using namespace std; #define int long long //#define int __int128 #define double long double typedef pair<int,int>PII; typedef pair<string,int>PSI; typedef pair<string,string>PSS; const int N=5e5+5,INF=0x3f3f3f3f,Mod=1e9+7,mod=998244353; const int MAXN=1e8+5; const double eps=1e-12; const int dx[4]={-1,1,0,0}; const int dy[4]={0,0,-1,1}; struct E{ int val; int son[2]; }; int n,k,idx; vector<E>tr; void insert(int val,int x){ int u=1; for(int i=30;i>=0;--i){ int s=(x>>i)&1; if(tr[u].son[s]==0)tr[u].son[s]=++idx; u=tr[u].son[s]; tr[u].val=(tr[u].val+val)%Mod; } } int get(int x){ int u=1; int res=0; for(int i=30;i>=0;--i){ int s=(x>>i)&1,m=(k>>i)&1; if(m){ if(tr[u].son[s])res=(res+tr[tr[u].son[s]].val)%Mod; u=tr[u].son[s^1]; }else u=tr[u].son[s]; } res=(res+tr[u].val)%Mod; return res; } int ksm(int a,int b){ int res=1; while(b){ if(b&1)res=res*a%Mod; b>>=1; a=a*a%Mod; } return res; } void solve() { cin>>n>>k; tr=vector<E>(n*32,{0,{0,0}}); idx=1; vector<int>a(n+1); for(int i=1;i<=n;++i){ cin>>a[i]; } sort(a.begin()+1,a.end()); int ans=1; for(int i=2;i<=n;++i){ int s=ksm(2,i-1); insert(ksm(s,Mod-2),a[i-1]); ans=(ans+1+get(a[i])*s%Mod)%Mod; } cout<<ans<<'\n'; } signed main(){ ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); int t=1; cin>>t; // init(); while(t--){ solve(); } return 0; }
DTokitsukaze and Slash Draw
思路:由于nm范围不大,可以操作每个位置使用每种卡片,跑最短路即可,就是求从0到n-k(位置从上到下为0、n-1、...2、1)
#include<bits/stdc++.h> using namespace std; #define int long long //#define int __int128 #define double long double typedef pair<int,int>PII; typedef pair<string,int>PSI; typedef pair<string,string>PSS; const int N=1e5+5,INF=0x3f3f3f3f,Mod=1e9+7,mod=998244353; const int MAXN=1e8+5; const double eps=1e-12; const int dx[4]={-1,1,0,0}; const int dy[4]={0,0,-1,1}; void solve() { int n,m,k; cin>>n>>m>>k; vector<vector<PII>>g(n); vector<PII>ve(m); vector<int>dis(n,1e17),vis(n); for(int i=0;i<m;++i)cin>>ve[i].first>>ve[i].second; for(int i=0;i<n;++i){ for(int j=0;j<m;++j){ int a=ve[j].first,b=ve[j].second; int to=(i+a)%n; g[i].push_back({to,b}); } } dis[0]=0; priority_queue<PII,vector<PII>,greater<PII>>q; q.push({dis[0],0}); while(q.size()){ auto t=q.top(); q.pop(); int u=t.second,dist=t.first; if(vis[u])continue; vis[u]=1; for(auto [v,w]:g[u]){ if(w+dist<dis[v]){ dis[v]=w+dist; q.push({dis[v],v}); } } } if(dis[n-k]==1e17)cout<<"-1\n"; else cout<<dis[n-k]<<'\n'; } signed main(){ ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); int t=1; cin>>t; // init(); while(t--){ solve(); } return 0; }
ETokitsukaze and Eliminate (easy)
思路:同hard
#include<bits/stdc++.h> using namespace std; #define int long long //#define int __int128 #define double long double typedef pair<int,int>PII; typedef pair<string,int>PSI; typedef pair<string,string>PSS; const int N=100+5,INF=0x3f3f3f3f,Mod=1e9+7,mod=998244353; const int MAXN=1e8+5; const double eps=1e-12; const int dx[4]={-1,1,0,0}; const int dy[4]={0,0,-1,1}; void solve() { int n; cin>>n; vector<int>ve(n); for(int i=0;i<n;++i){ cin>>ve[i]; } int ans=0; for(int i=n-1;i>=0;--i){ int c=0,r=ve[i]; while(i>=0&&ve[i]==r)i--,c++; if(i==-1)ans+=c; else ans++; } cout<<ans<<'\n'; } signed main(){ ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); int t=1; cin>>t; while(t--){ solve(); } return 0; }
FTokitsukaze and Eliminate (hard)
思路:从后往前枚举,找到要操作的颜色。其实就是统计每个颜色的最后一个的位置的最小值即为操作的颜色,再更新每个颜色的最后一个的位置
#include<bits/stdc++.h> using namespace std; #define int long long //#define int __int128 #define double long double typedef pair<int,int>PII; typedef pair<string,int>PSI; typedef pair<string,string>PSS; const int N=100+5,INF=0x3f3f3f3f,Mod=1e9+7,mod=998244353; const int MAXN=1e8+5; const double eps=1e-12; const int dx[4]={-1,1,0,0}; const int dy[4]={0,0,-1,1}; void solve() { int n; cin>>n; vector<int>ve(n+1),l(n+1),st(n+1); for(int i=1;i<=n;++i){ cin>>ve[i]; l[i]=st[ve[i]]; st[ve[i]]=i; } vector<int>now; st=vector<int>(n+1,0); int mi=n; for(int i=n;i>=1;--i){ if(!st[ve[i]])now.push_back(i),mi=i; st[ve[i]]=1; } int ans=0; while(mi>0){ ans++; if(mi==1)break; int s=n; vector<int>p; for(auto &v:now){ if(v==0)continue; while(v>=mi&&v!=0)v=l[v]; if(v!=0){ s=min(s,v); p.push_back(v); } } mi=s; now=p; } cout<<ans<<'\n'; } signed main(){ ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); int t=1; cin>>t; while(t--){ solve(); } return 0; }
GTokitsukaze and Power Battle (easy)
思路:对于区间[l,r],要选取两个区间[i,x]和[x,j],使得f[x]=sum[i,x]-sum[x+1,j]最大,由于都为正数,那第一个区间的数要尽可能多,第二个区间的数尽可能少,所以第二个区间只有一个数、第一个区间的左边界为l且个数多是最优的。那么j就从r往前枚举,i就为l,且x+1等于j,满足i<j<=r。
如果暴力枚举所有j肯定会T,由于f[j] = sum[l,j-1]-a[j],看下f[j-1] = sum[l,j-2] - a[j-1] = f[j] - 2a[j-1] + a[j],要是答案更优需满足f[j-1]>f[j],即a[j-1]<a[j]/2,由于a的范围为1e9,那么j枚举不超过31次,就可以树状数组直接枚举答案了
--------------
还有个思路,还是看这个f[j] = sum[l,j-1]-a[j],因为l是定的,那么其实f[j]=sum[1,j-1]-a[j]-sum[1,l-1],枚举的所有j都是要减去sum[1,l-1],其实就对sum[1,j-1]-a[j]用线段树维护区间最大值就可以O1求答案了
对于单点修改x、y,求前缀和的直接用树状数组维护;求区间最大的线段树,单点修改只对x及之后的位置影响,由sum[1,j-1]-a[j],对位置x,其实就是加回原来的a[j]再减去修改的y,为a[j]-y;对位置x+1到n,就是减去原来的a[j]再加上修改的y,为y-a[j],这里用区间修改即可
#include<bits/stdc++.h> using namespace std; #define int long long //#define int __int128 #define double long double typedef pair<int,int>PII; typedef pair<string,int>PSI; typedef pair<string,string>PSS; const int N=1e3+5,INF=0x3f3f3f3f,Mod=1e9+7,mod=998244353; const int MAXN=1e8+5; const double eps=1e-12; const int dx[4]={-1,1,0,0}; const int dy[4]={0,0,-1,1}; vector<int>c; int n; int lowbit(int x){ return x&-x; } int getsum(int x){//a[1]..a[x]的和 int ans=0; while(x>0){ ans+=c[x]; x-=lowbit(x); } return ans; } int get(int l,int r){ return getsum(r)-getsum(l-1); } void add(int x,int k){ while(x<=n){ c[x]+=k; x+=lowbit(x); } } void solve() { cin>>n; int q; cin>>q; c=vector<int>(n+5,0); for(int i=1;i<=n;++i){ int x; cin>>x; add(i,x); } while(q--){ int op,l,r; cin>>op>>l>>r; if(op==1){ int s= r-get(l,l); add(l,s); }else{ int s= get(l,r); int ans=-INF; for(int i=r;i>l;--i){ int rr= get(i,i); s-=rr; if(s<=ans)break; int x=s-rr; ans=max(ans,x); } cout<<ans<<'\n'; } } } signed main(){ ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); int t=1; cin>>t; // init(); while(t--){ solve(); } return 0; }
#include<bits/stdc++.h> using namespace std; #define int long long //#define int __int128 #define double long double typedef pair<int,int>PII; typedef pair<string,int>PSI; typedef pair<string,string>PSS; const int N=5e5+5,INF=0x3f3f3f3f,Mod=1e9+7,mod=998244353; const int MAXN=1e8+5; const double eps=1e-12; const int dx[4]={-1,1,0,0}; const int dy[4]={0,0,-1,1}; vector<int>c; int n;//每个数的值 int lowbit(int x){ return x&-x; } int getsum(int x){//a[1]..a[x]的和 int ans=0; while(x>0){ ans+=c[x]; x-=lowbit(x); } return ans; } void add(int x,int k){ while(x<=n){ c[x]+=k; x+=lowbit(x); } } struct Node{ int l,r; int ma; int add;//懒标记 }; vector<int>w; vector<Node>tr; //pushup void pushup(int u){ tr[u].ma=max(tr[u<<1].ma,tr[u<<1|1].ma); } //pushdown:除建树外,分裂都需要用 void pushdown(int u){ Node &root=tr[u],&l=tr[u<<1],&r=tr[u<<1|1]; l.add+=root.add,l.ma+=root.add; r.add+=root.add,r.ma+=root.add; root.add=0; } //建树 void build(int u,int l,int r){ if(l==r)tr[u]={l,r,getsum(l-1)-w[l],0}; else{ tr[u]={l,r,0,0}; int mid=l+r>>1; build(u<<1,l,mid),build(u<<1|1,mid+1,r); pushup(u); } }; //区间修改:区间[l,r]加d void modify(int u,int l,int r,int d){ if(tr[u].l>=l&&tr[u].r<=r){ tr[u].ma+=d; tr[u].add+=d; } else{ pushdown(u); int mid=tr[u].l+tr[u].r>>1; if(l<=mid)modify(u<<1,l,r,d); if(r>mid)modify(u<<1|1,l,r,d); pushup(u); } } //求[l,r]区间和 int query(int u,int l,int r){ if(tr[u].l>=l&&tr[u].r<=r)return tr[u].ma; else if(r<tr[u].l||tr[u].r<l)return -1e18; else{ pushdown(u); return max(query(u<<1,l,r),query(u<<1|1,l,r)); } } void solve() { cin>>n; int q; cin>>q; tr=vector<Node>(4*n+5); c=w=vector<int>(n+5,0); for(int i=1;i<=n;++i) { cin >> w[i]; add(i,w[i]); } build(1,1,n); while(q--){ int op,l,r; cin>>op>>l>>r; if(op==1){ modify(1,l,l,w[l]-r); if(l<n)modify(1,l+1,n,r-w[l]); add(l,r-w[l]); w[l]=r; }else{ cout<<query(1,l+1,r)- getsum(l-1)<<'\n'; } } } signed main(){ ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); int t=1; cin>>t; // init(); while(t--){ solve(); } return 0; }
ITokitsukaze and Short Path (plus)
思路:由权值计算式子可以看出路径长度越长权值越大,那么dist(i,j)最小即为i到j
#include<bits/stdc++.h> using namespace std; #define int long long //#define int __int128 #define double long double typedef pair<int,int>PII; typedef pair<string,int>PSI; typedef pair<string,string>PSS; const int N=100+5,INF=0x3f3f3f3f,Mod=1e9+7,mod=998244353; const int MAXN=1e8+5; const double eps=1e-12; const int dx[4]={-1,1,0,0}; const int dy[4]={0,0,-1,1}; void solve() { int n,ans=0; cin>>n; vector<int>ve(n+1),pre(n+1); for(int i=1;i<=n;++i){ cin>>ve[i]; ans+=ve[i]*(2*n-2); } sort(ve.begin()+1,ve.end()); for(int i=1;i<=n;++i)pre[i]=pre[i-1]+ve[i]; for(int i=1;i<=n;++i){ ans+=pre[n]-pre[i]-(n-i)*ve[i]+(i-1)*ve[i]-pre[i-1]; } cout<<ans<<'\n'; } signed main(){ ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); int t=1; cin>>t; while(t--){ solve(); } return 0; }
JTokitsukaze and Short Path (minus)
思路:由权值计算式子可以看出wij为2*min(ai,aj),那么dist(i,j)有两种情况,一种为2*min(ai,aj),另一种为找到权值最小的点q,dist(i,j)= dist(i,q)+ dist(q,j)=4*aq,取较小值即可
#include<bits/stdc++.h> using namespace std; #define int long long //#define int __int128 #define double long double typedef pair<int,int>PII; typedef pair<string,int>PSI; typedef pair<string,string>PSS; const int N=100+5,INF=0x3f3f3f3f,Mod=1e9+7,mod=998244353; const int MAXN=1e8+5; const double eps=1e-12; const int dx[4]={-1,1,0,0}; const int dy[4]={0,0,-1,1}; void solve() { int n; cin>>n; vector<int>ve(n); for(int i=0;i<n;++i){ cin>>ve[i]; } sort(ve.begin(),ve.end()); int mi=ve[0]; int ans=(n-1)*2*mi,p=4*mi; for(int i=1;i<n;++i){ if(2*ve[i]<p)ans+=2*ve[i]*(n-i-1); else ans+=p*(n-i-1); } cout<<ans*2<<'\n'; } signed main(){ ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); int t=1; cin>>t; while(t--){ solve(); } return 0; }
KTokitsukaze and Password (easy)
思路:n不大,暴力枚举
#include<bits/stdc++.h> using namespace std; #define int long long //#define int __int128 #define double long double typedef pair<int,int>PII; typedef pair<string,int>PSI; typedef pair<string,string>PSS; const int N=1e5+5,INF=0x3f3f3f3f,Mod=1e9+7,mod=998244353; const int MAXN=1e8+5; const double eps=1e-12; const int dx[4]={-1,1,0,0}; const int dy[4]={0,0,-1,1}; void solve() { int n,y; set<int>ans; string s; cin>>n>>s>>y; char a,b,c,d,e; for(a='0';a<='9';++a){ for(b='0';b<='9';++b){ for(c='0';c<='9';++c){ for(d='0';d<='9';++d){ for(e='0';e<='9';++e){ if(a==b||b==c||c==d||a==d||a==c||b==d)continue; string t=s; for(int i=0;i<t.size();++i){ if(t[i]=='a')t[i]=a; else if(t[i]=='b')t[i]=b; else if(t[i]=='c')t[i]=c; else if(t[i]=='d')t[i]=d; else if(t[i]=='_')t[i]=e; } if(n>1&&t[0]=='0')continue; int x=stoi(t); if(x%8==0&&x<=y)ans.insert(x); } } } } } cout<<ans.size()<<'\n'; } signed main(){ ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); int t=1; cin>>t; // init(); while(t--){ solve(); } return 0; }