本文主要是介绍2021牛客寒假集训营4,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
B.武辰延的字符串
思路:
比赛的时候一直想着要用到kmp,其实并不需要。题目要我们求的其实就是t i+j = s i+s j。那么只要枚举s和t的所有相同前缀,然后二分查找 t - si ,找到能匹配上s的前缀 sj 的最大长度,计入答案即可。
具体算法就是哈希和二分:
-
哈希能够快速O(1)判断两个子串是否匹配
-
二分是因为我们在找到某个前缀匹配时,前缀的前缀也一定匹配,所以可以二分找到最长前缀,因为比它长的前缀都不匹配,比它短的所有前缀都匹配,这就满足二分使用前提的单调性。
AC Code
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=1e5+10,mod=1e9+7;
LL sum1[N],sum2[N],fac[N],base=131;
LL ans1,ans2;
char s[N],t[N];
void check(int l,int r)
{ans1=sum1[r-l+1];ans2=(sum2[r]-sum2[l-1]*fac[r-l+1]%mod+mod)%mod;
}
int main()
{cin>>s+1>>t+1;int n=strlen(s+1),m=strlen(t+1);for(int i=1;i<=n;i++)sum1[i]=sum1[i-1]*base%mod+(s[i]-'a'+1);for(int i=1;i<=m;i++)sum2[i]=sum2[i-1]*base%mod+(t[i]-'a'+1);fac[0]=1;for(int i=1;i<=max(n,m);i++)fac[i]=fac[i-1]*base%mod;int k=min(n,m);LL res=0;for(int i=1;i<=k;i++){if(s[i]!=t[i]) break;if(s[1]!=t[i+1]) continue;LL l=i+1,r=min(m,i+n);while(l<r){LL mid=(l+r+1)/2;check(i+1,mid);if(ans1==ans2) l=mid;else r=mid-1;}res=res+l-i;}printf("%lld\n",res);system("pause");return 0;
}
F魏迟燕的自走棋
解法1:贪心+并查集
贪心很显然就是战力增加多的装备优先选择。由于每个装备最多试用与两个人,所以可以用并查集维护关系。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=1e5+10;
struct node
{int idx;int w;
}a[N];
int num[N][5];
int siz[N],cnt[N],fa[N];
bool cmp(node a,node b)
{return a.w>b.w;
}
int find(int x)
{if(x==fa[x]) return x;return fa[x]=find(fa[x]);
}
int main()
{int n,m;scanf("%d%d",&n,&m);for(int i=1;i<=m;i++){int k;scanf("%d",&k);for(int j=1;j<=k;j++) scanf("%d",&num[i][j]);scanf("%d",&a[i].w);a[i].idx=i;}sort(a+1,a+1+m,cmp);for(int i=1;i<=n;i++){fa[i]=i;siz[i]=1;} LL res=0;for(int i=1;i<=m;i++){int id=a[i].idx;if(num[id][2]==0){int fx=find(num[id][1]);if(cnt[fx]<siz[fx]){res=res+a[i].w;cnt[fx]++;}}else{int fx=find(num[id][1]);int fy=find(num[id][2]);if(fx!=fy&&(cnt[fx]<siz[fx]||cnt[fy]<siz[fy])){siz[fx]+=siz[fy];fa[fy]=fx;res=res+a[i].w;cnt[fx]+=cnt[fy]+1;}else if(fx==fy&&cnt[fx]<siz[fx]){res=res+a[i].w;cnt[fx]++;}}}printf("%lld\n",res);system("pause");return 0;
}
解法2:贪心+匈牙利算法
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=1e5+10;
struct node
{int idx;int w;
}a[N];
int num[N][5];
int used[N],ans[N];
bool cmp(node a,node b)
{return a.w>b.w;
}
bool found(int x)
{for(int i=1;i<=2;i++){int to=num[x][i];if(!used[to]&&to!=0){used[to]=1;if(ans[to]==0||found(ans[to])){ans[to]=x;used[to]=0;return 1;}}}return 0;
}
int main()
{int n,m;scanf("%d%d",&n,&m);for(int i=1;i<=m;i++){int k;scanf("%d",&k);for(int j=1;j<=k;j++) scanf("%d",&num[i][j]);scanf("%d",&a[i].w);a[i].idx=i;}sort(a+1,a+1+m,cmp);LL res=0;for(int i=1;i<=m;i++){int id=a[i].idx;if(found(id)) res+=a[i].w;}printf("%lld\n",res);system("pause");return 0;
}
G九峰与蛇形填数
思路:
- 暴力,每个点取值取到的肯定是最后一个覆盖到它的区域,所以直接取那个数即可。还有一个细节就是剪枝,先预处理区域的大小,如果这个点不在区域则直接break,不用赋值。
(但我觉得这个题数据如果再恶心点,暴利应该是不行的,所以下面还有线段树维护的做法)
AC Code
#include<bits/stdc++.h>
using namespace std;
const int N=3e3+10;
int x[N],y[N],k[N];
int maze[N][N];
int main()
{int n,m;scanf("%d%d",&n,&m);int maxx=0,maxy=0,minx=2500,miny=2500;for(int i=1;i<=m;i++){scanf("%d%d%d",&x[i],&y[i],&k[i]);maxx=max(maxx,x[i]+k[i]-1);minx=min(minx,x[i]);maxy=max(maxy,y[i]+k[i]-1);miny=min(miny,y[i]);}for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){for(int l=m;l>=1;l--){if(i<minx||i>maxx||j<miny||j>maxy) break;if(i>=x[l]&&i<=x[l]+k[l]-1&&j>=y[l]&&j<=y[l]+k[l]-1){maze[i][j]=k[l]*(i-x[l]);if (i - x[l] & 1) maze[i][j] += y[l] + k[l] - j;else maze[i][j] += j - y[l] + 1;break;}}}}for(int i=1;i<=n;i++){for(int j=1;j<=n;j++)printf("%d ",maze[i][j]);printf("\n");}//system("pause");return 0;
}
可以开2000棵线段树(每一个横坐标开一棵线段树),然后用lazy标记维护区间。时间复杂度应该是nmlog(n)。但是奇怪的是,就这题来说暴力要比这稍微快一点。
AC Code
#include<bits/stdc++.h>
using namespace std;
const int N=2e3+5,M=3e3+10;
struct node
{struct point{int l,r,lazy;}tr[N<<2];void pushdown(int k){if(tr[k].lazy){tr[k<<1].lazy=tr[k].lazy;tr[k<<1|1].lazy=tr[k].lazy;tr[k].lazy=0;}}void build(int k,int l,int r){tr[k].l=l,tr[k].r=r;tr[k].lazy=0;if(l==r) return ;int mid=(l+r)>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);}void modify(int k,int l,int r,int x){if(l<=tr[k].l&&r>=tr[k].r){tr[k].lazy=x;return ;}pushdown(k);int mid=(tr[k].l+tr[k].r)>>1;if(l<=mid) modify(k<<1,l,r,x);if(r>=mid+1) modify(k<<1|1,l,r,x);}int query(int k,int x){if(tr[k].l==tr[k].r)return tr[k].lazy;pushdown(k);int mid=(tr[k].l+tr[k].r)>>1;if(x<=mid) return query(k<<1,x);else return query(k<<1|1,x);}
}tree[N];
int x[M],y[M],k[M];
void solve(int l,int i,int j)
{int res=k[l]*(i-x[l]);if (i - x[l] & 1) res+=y[l]+k[l]-j;else res+=j-y[l]+1;printf("%d ",res);
}
int main()
{int n,m;scanf("%d%d",&n,&m);for(int i=1;i<=n;i++)tree[i].build(1,1,n);for(int i=1;i<=m;i++){scanf("%d%d%d",&x[i],&y[i],&k[i]);for(int j=x[i];j<=x[i]+k[i]-1;j++)tree[j].modify(1,y[i],y[i]+k[i]-1,i);}for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){int f=tree[i].query(1,j);if(f==0) printf("0 ");else solve(f,i,j);}printf("\n");}//system("pause");return 0;
}
H.吴楚月的表达式
思路:
每一个表达式都可以化作是多个表达式的和。比如一个非空的表达式前缀可以表示成a+b的形式
- 如果后面接了一个+x,则变成a+b+x
- 如果后面接了一个-x,则变成了a+b+(-x)
- 如果后面接了一个x,则变成了a+bx
- 如果后面接了一个/x,则变成了a+b/x
所以不管怎么样都可以变成a+b的形式。那么假设当前节点为x,父节点为fa,显然如果fa->x的符号是 + 或者是 - 的话,直接从res[fa]+w[x]或res[fa]-w[x]即可,同时将b更新为w[x]或是-[x]。但是当符号是*或/时,那么它就会与fa的b扯上关系,那么就要先减去然后重新加上。具体细节见代码
AC Code
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=1e5+10;
const LL mod=1e9+7;
LL w[N],fa[N];
char op[N];
int head[N],cent;
LL quick_pow(LL x,LL y)
{LL ans=1;while(y){if(y%2) ans=ans*x%mod;x=x*x%mod;y=y/2;}return ans;
}
struct node
{int to,next;
}tr[N];
void add(int u,int v)
{tr[cent].to=v;tr[cent].next=head[u];head[u]=cent++;
}
LL res[N],num[N];
void dfs(int x)
{for(int i=head[x];~i;i=tr[i].next){int to=tr[i].to;if(op[to-1]=='+'){res[to]=(res[x]+w[to])%mod;num[to]=w[to]%mod;}else if(op[to-1]=='-'){res[to]=(res[x]-w[to]+mod)%mod;num[to]=(-1*w[to]+mod)%mod;}else if(op[to-1]=='*'){res[to]=(res[x]-num[x]+num[x]*w[to]%mod+mod)%mod;num[to]=num[x]*w[to]%mod;}else if(op[to-1]=='/'){res[to]=(res[x]-num[x]+num[x]*quick_pow(w[to],mod-2)%mod+mod)%mod;num[to]=num[x]*quick_pow(w[to],mod-2)%mod;}dfs(to);}
}
int main()
{int n;scanf("%d",&n);for(int i=1;i<=n;i++)scanf("%lld",&w[i]);for(int i=1;i<n;i++)scanf("%d",&fa[i]);for(int i=1;i<n;i++)cin>>op[i];memset(head,-1,sizeof(head));for(int i=1;i<n;i++)add(fa[i],i+1);res[1]=w[1];num[1]=w[1];dfs(1);for(int i=1;i<=n;i++)printf("%lld ",res[i]);//system("pause");return 0;
}
这篇关于2021牛客寒假集训营4的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!