目录
目录
涵盖知识点:贪心、动态规划、树链剖分。
比赛链接:传送门
好久之前的比赛了,因为不会树剖拖到现在>_<
A-牛能和宝石
题解: 对数组(a)正向排序,数组(b)逆向排序,求和取最大值即可。
Accept Code:
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int a[maxn],b[maxn];
int main(){
int n;
cin>>n;
for(int i=0;i<n;i++)
cin>>a[i];
for(int i=0;i<n;i++)
cin>>b[i];
sort(a,a+n);
sort(b,b+n,greater<int>());
int ans=0;
for(int i=0;i<n;i++)
ans=max(ans,a[i]+b[i]);
cout<<ans<<"
";
return 0;
}
B-牛妹和01串
题解: 贪心,扫描一遍遇到最短的串满足情况重新计算即可。
Accept Code:
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int main(){
string s;
cin>>s;
int res=0,flag=s[0]-'0';
for(int i=0;i<s.length();i++){
if(s[i]-'0'==flag)continue;
res++;
flag=s[i+1]-'0';
}
cout<<res<<"
";
return 0;
}
C-矩阵消除游戏
题解: 首先若(kge min(n,m)),不难得出我们可以消除矩阵内所有的元素。然后在(kle min(n,m))的情况下我们通过二进制枚举所选择行的情况,然后对每一列剩余元素之和进行排序,贪心选取,取最大值即可。
Accept Code:
#include <bits/stdc++.h>
using namespace std;
const int maxn=20;
int a[maxn][maxn],sum[maxn];
int main(){
int n,m,k;
cin>>n>>m>>k;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>a[i][j];
}
}
k=min(k,min(n,m));
int ans=0;
for(int i=0;i<1<<n;i++){
int cnt=__builtin_popcount(i);
memset(sum,0,sizeof sum);
if(cnt>k)continue;
int res=0;
for(int j=1;j<=m;j++){
for(int l=1;l<=n;l++){
if(1<<(l-1)&i)res+=a[l][j];
else sum[j]+=a[l][j];
}
}
sort(sum+1,sum+1+m,greater<int>());
for(int j=1;j<=k-cnt;j++){
res+=sum[j];
}
ans=max(ans,res);
}
cout<<ans<<"
";
return 0;
}
D-迷宫
题解: 根据题意可以得出牛妹只能优先向右走,然后向下走。证明见下方。所以就是简单动态规划。
转移方程:
[egin{cases}
dp_{i,j+1}=dp_{i,j} qquad (mp_{i,j+1}
e 0)\
dp_{i+1,j}=dp_{i,j}+[mp_{i,j+1}==0]
end{cases}]
证明:
1. 若牛妹向左走,则下一步一定可以向右走。无意义。
2. 若牛妹向上走,则说明当前不能向右或向下。说明前一步一定是向右或者向下。若向右,则会优先向左走,见情况1;若向下则本步向上无意义。
Accept Code:
#include <bits/stdc++.h>
using namespace std;
const int maxn=1010;
int dp[maxn][maxn];
char mp[maxn][maxn];
const int inf=0x3f3f3f3f;
int main(){
memset(dp,inf,sizeof dp);
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>(mp[i]+1);
}
dp[1][1]=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(mp[i][j]=='1')continue;
if(j<m&&mp[i][j+1]=='0')dp[i][j+1]=min(dp[i][j+1],dp[i][j]);
if(i<n&&mp[i+1][j]=='0'){
if(j==m||mp[i][j+1]=='1')dp[i+1][j]=min(dp[i][j+1],dp[i][j]);
else dp[i+1][j]=min(dp[i+1][j],dp[i][j]+1);
}
}
}
if(dp[n][m]==inf)cout<<"-1
";
else cout<<dp[n][m]<<"
";
return 0;
}
E-最大GCD
题解: 一个区间和(x)的最大(gcd)一定是区间内某一个数和(x)的(gcd)。所以我们预处理一下每个因子所在的全部下标。然后对于(x)的全部因子都check一遍取最大值即可。
Accept Code:
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
vector<int> v[maxn];
int a[maxn];
int check(int x,int l,int r){
int idx=lower_bound(v[x].begin(),v[x].end(),l)-v[x].begin();
if(idx==v[x].size())return -1;
if(v[x][idx]<=r)return x;
return -1;
}
int main(){
int n,q;
cin>>n>>q;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
for(int j=1;j<=sqrt(a[i]);j++){
if(a[i]%j==0){
if(j*j==a[i])v[j].push_back(i);
else{
v[j].push_back(i);
v[a[i]/j].push_back(i);
}
}
}
}
while(q--){
int ans=1;
int l,r,x;
scanf("%d%d%d",&l,&r,&x);
for(int i=1;i<=sqrt(x);i++){
if(x%i==0){
if(i*i==x)ans=max(ans,check(i,l,r));
else{
ans=max(ans,check(i,l,r));
ans=max(ans,check(x/i,l,r));
}
}
}
printf("%d
",ans);
}
return 0;
}
F-XOR TREE
题解: 根据两个相同数字异或一遍为0的规律,我们不难发现如果路径长度是奇数就是所有点权值的异或值,如果路径长度是偶数就是隔一个点取异或值。所以我们可以树剖一下,然后根据节点深度维护两棵线段树分别记录奇数深度和偶数深度的路径异或和即可。
Accept Code:
#include <bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
int a[maxn];
int read(){
int x=0;
char c=getchar();
while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+c-'0',c=getchar();
return x;
}
vector<int> edg[maxn];
int f[maxn],d[maxn],siz[maxn],son[maxn],rk[maxn],top[maxn],id[maxn],cnt;
void dfs1(int u,int fa,int dep){
f[u]=fa;
d[u]=dep;
siz[u]=1;
for(auto v:edg[u]){
if(v==fa)continue;
dfs1(v,u,dep+1);
siz[u]+=siz[v];
if(siz[v]>siz[son[u]])
son[u]=v;
}
}
void dfs2(int u,int t){
top[u]=t;
id[u]=++cnt;
rk[cnt]=u;
if(!son[u])return;
dfs2(son[u],t);
for(auto v:edg[u]){
if(v!=son[u]&&v!=f[u])
dfs2(v,v);
}
}
struct Node{
int l,r,sum;
}tree[2][maxn<<2];
inline int ls(int x){return x<<1;}
inline int rs(int x){return x<<1|1;}
void pushup(int root,int idx){
tree[idx][root].sum=tree[idx][ls(root)].sum^tree[idx][rs(root)].sum;
}
void build(int l,int r,int root,int idx){
tree[idx][root].l=l,tree[idx][root].r=r;
if(l==r){
if((d[rk[l]]&1)==(idx&1)){
tree[idx][root].sum=a[rk[l]];
} else{
tree[idx][root].sum=0;
}
return;
}
int mid=(l+r)>>1;
build(l,mid,ls(root),idx);
build(mid+1,r,rs(root),idx);
pushup(root,idx);
}
void update(int l,int r,int root,int pos,int val,int idx){
if(l==r){
tree[idx][root].sum=val;
return;
}
int mid=(l+r)>>1;
if(pos<=mid){
update(l,mid,ls(root),pos,val,idx);
}else{
update(mid+1,r,rs(root),pos,val,idx);
}
pushup(root,idx);
}
int query(int l,int r,int root,int ql,int qr,int idx){
if(qr<l||ql>r)return 0;
if(ql<=l&&qr>=r)return tree[idx][root].sum;
int mid=(l+r)>>1;
return query(l,mid,ls(root),ql,qr,idx)^query(mid+1,r,rs(root),ql,qr,idx);
}
int main(){
int n=read();
int q=read();
for(int i=1;i<=n;i++)a[i]=read();
for(int i=1;i<=n-1;i++){
int u=read();
int v=read();
edg[u].push_back(v);
edg[v].push_back(u);
}
dfs1(1,0,1);
dfs2(1,1);
build(1,n,1,0);
build(1,n,1,1);
while(q--){
int idx=read();
int u=read();
int v=read();
if(idx==1){
update(1,n,1,id[u],v,d[u]&1);
}
else{
int ans=0;
if(abs(d[u]-d[v])&1){
while(top[u]!=top[v]){
if(d[top[u]]<d[top[v]])swap(u,v);
ans^=query(1,n,1,id[top[u]],id[u],0);
ans^=query(1,n,1,id[top[u]],id[u],1);
u=f[top[u]];
}
if(d[u]>d[v])swap(u,v);
ans^=query(1,n,1,id[u],id[v],0);
ans^=query(1,n,1,id[u],id[v],1);
}else{
int idx=(d[u]+1)&1;
while(top[u]!=top[v]){
if(d[top[u]]<d[top[v]])swap(u,v);
ans^=query(1,n,1,id[top[u]],id[u],idx);
u=f[top[u]];
}
if(d[u]>d[v])swap(u,v);
ans^=query(1,n,1,id[u],id[v],idx);
}
printf("%d
",ans);
}
}
return 0;
}