Atcoder-Beginner-Contest-312 A~Ex
AB过于简单,在此略去。
题意:给定长为
题解:
容易发现,随着
将两数组排序,二分答案。可以使用 lower_bound
和 upper_bound
快速统计出左右两式。
sort(a+1,a+n+1);sort(b+1,b+m+1);
int l=0,r=0x3f3f3f3f;
while(l<r){
int mid=l+r>>1;
int x=upper_bound(a+1,a+n+1,mid)-a-1;
int y=lower_bound(b+1,b+m+1,mid)-b;
y=m+1-y;
if(x>=y)r=mid;
else l=mid+1;
}
cout<<l<<"\n";
题意:给定一个残缺的由
合法括号序列的充要条件是在任意前缀中左括号数量不小于右括号数量,且左括号总数量等于右括号总数量。
复杂度要求
考虑设
初始化:
目标:
转移:
注意边界。
f[0][0]=1;
for(int i=1;i<=n;i++){
if(a[i]=='(') for(int j=1;j<=n;j++)f[i][j]=f[i-1][j-1];
else if(a[i]==')') for(int j=0;j<n;j++)f[i][j]=f[i-1][j+1];
else for(int j=1;j<=n;j++)f[i][j]=(f[i-1][j+1]+f[i-1][j-1])%p;
}
cout<<f[n][0]<<"\n";
题意:给定若干不相交的长方体,坐标范围在
考虑弱化条件为对于每个长方体而言,有多少个长方体与它共用
需要注意的是,对于右上顶点坐标需要都减去1,这很容易理解,避免长方体交。
等下,我们要求的就是交一个平面。那么,什么样的情况下才能共用一个
那么其实与
那么我们枚举这个坐标就可以解决问题了
#include<iostream>
#include<algorithm>
#include<set>
using namespace std;
#define N 505050
#define M 105
set<int>ans[N];
int n,a[M][M][M];
int main(){
ios::sync_with_stdio(false);
cin>>n;
for(int p=1;p<=n;p++){
int x1,y1,z1,x2,y2,z2;
cin>>x1>>y1>>z1>>x2>>y2>>z2;
for(int i=x1;i<x2;i++)for(int j=y1;j<y2;j++)for(int k=z1;k<z2;k++)a[i][j][k]=p;
}
for(int i=0;i<=100;i++)for(int j=0;j<=100;j++)for(int k=0;k<=100;k++){
if(!a[i][j][k])continue;
if(a[i+1][j][k]&&a[i+1][j][k]!=a[i][j][k])ans[a[i][j][k]].insert(a[i+1][j][k]),ans[a[i+1][j][k]].insert(a[i][j][k]);
if(a[i][j+1][k]&&a[i][j+1][k]!=a[i][j][k])ans[a[i][j][k]].insert(a[i][j+1][k]),ans[a[i][j+1][k]].insert(a[i][j][k]);
if(a[i][j][k+1]&&a[i][j][k+1]!=a[i][j][k])ans[a[i][j][k]].insert(a[i][j][k+1]),ans[a[i][j][k+1]].insert(a[i][j][k]);
}
for(int i=1;i<=n;i++)cout<<ans[i].size()<<"\n";
}
题意:给定
- 第一类罐子,不需要开罐器,收益为
- 第二类罐子,需要开罐器,收益为
- 开罐器,可以用
次。
求从
我们现将3类物品分开,分别记为
设
#define int long long
#define N 505050
void read(int &x){
x=0;char ch=getchar();int w=1;
while(ch>'9'||ch<'0'){
if(ch=='-')w=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
x*=w;
}
int n,ans,m,f[N],a[N],b[N],c[N],n1,n2,n3;
signed main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
int t;cin>>t;if(t==0)cin>>a[++n1];
else if(t==1)cin>>b[++n2];
else cin>>c[++n3];
}
sort(a+1,a+n1+1);sort(b+1,b+n2+1);sort(c+1,c+n3+1);
reverse(a+1,a+n1+1);reverse(b+1,b+n2+1);reverse(c+1,c+n3+1);
for(int i=1;i<=m;i++)a[i]+=a[i-1];
int j=0,sur=0,k=0;
for(int i=1;i<=m;i++){
if(!sur)f[i]=f[i-1],sur=c[++j];
else f[i]=f[i-1]+b[++k],--sur;
}
for(int i=0;i<=m;i++)ans=max(ans,f[i]+a[m-i]);
cout<<ans<<"\n";
}
题意:给定一颗树,求满足
简单的树形DP问题,不知为什么题解那么复杂。
发现不在同一简单路径比较复杂,考虑容斥。则显然总的点对数是容易求出的,为
考虑固定路径中点
形式地说,设
每一个乘积式的左边是在当前子树里选,后半部分表示在其他部分选,然后除以二是因为重复统计了。
所以代码很简单了。
#define int long long
#define N 505050
void read(int &x){
x=0;char ch=getchar();int w=1;
while(ch>'9'||ch<'0'){
if(ch=='-')w=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
x*=w;
}
int head[N],ver[N],nxt[N],tot,siz[N];
void add(int u,int v){
nxt[++tot]=head[u],ver[head[u]=tot]=v;
}
int n,ans;
void dfs(int u,int fa){
siz[u]=1;
int k=0;
for(int i=head[u];i;i=nxt[i]){
int v=ver[i];if(v!=fa){
dfs(v,u);siz[u]+=siz[v];
k+=siz[v]*(n-siz[v]-1);
}
}
k+=(n-siz[u])*(siz[u]-1);
ans-=k/2;
}
signed main(){
cin>>n;
for(int i=1;i<n;i++){
int u,v;cin>>u>>v;add(u,v);add(v,u);
}ans=n*(n-1)*(n-2)/2/3;
dfs(1,0);
cout<<ans<<"\n";
}
题意:
有
对于每个
题解:
我们考虑什么情况下前面的人可能会占用后面的人的名字——前面的人和后面的人有共同的最小循环元。
用 Z函数处理出每个人名字的最小循环元,然后按照最小循环元,以编号顺序分组,依次解决每一组。
对于每一组,我们只需记录原
维护一个含有若干整数的集合
对于每个
这个
这是一个经典的问题。倍数法的时间复杂度是
注意到对
这是容易的,记录一个
注意到倍数可能会很大,存不下,并且判断相同的循环元等我们需要用到STL容器比如map等,而且枚举倍数法时候,标记数组
#define N 505050
#define pr pair<int,int>
#define x first
#define id second
#define mk make_pair
char a[N];
int cnt[N],z[N],n,ans[N],t,num,lst[N];
map<int,int>vis;
map<string,int>h;
vector<pr >c[N];
void init_str(int id){
int n=strlen(a+1);
for(int i=1;i<=n+1;i++)z[i]=0;
if(n==1){
string x;x+=a[1];
if(!h[x])h[x]=++num;
c[h[x]].push_back(mk(1,id));return ;
}
z[1]=n;int l=0,r=0;
for(int i=2;i<=n;i++){
if(i<=r)z[i]=min(z[i-l+1],r-i+1);
while(i+z[i]<=n&&a[z[i]+1]==a[i+z[i]])++z[i];
if(i+z[i]-1>r)r=i+z[i]-1,l=i;
}z[n+1]=0;
for(int i=2;i<=n+1;i++){
if(i+z[i]-1==n&&n%(i-1)==0){
string x;
for(int j=1;j<i;j++)x+=a[j];
if(!h[x])h[x]=++num;
c[h[x]].push_back(mk(n/(i-1),id));
return ;
}
}
}
void solve(int id){
for(auto x:c[id]){
int i=x.x;
if(lst[x.x]!=0)i=lst[x.x]+x.x;
for(;i;i+=x.x){
if(vis[i]!=id){
lst[x.x]=i;vis[i]=id;ans[x.id]=i/x.x;
break;
}
}
}
for(auto x:c[id])lst[x.x]=0;
}
int main(){
ios::sync_with_stdio(false);
cin>>n;for(int i=1;i<=n;i++){
cin>>a+1;
init_str(i);
}
for(int i=1;i<=num;i++)solve(i);
for(int i=1;i<=n;i++)cout<<ans[i]<<" ";
}
总结
很简单,没什么好说的。
- 利用体积空间较小性质解题
- 容斥原理
- Z函数,KMP以及倍数法时间渐进
的结论。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!