A
思路
至多只能操作一次,只需要判断A和B是否只有一段连续区间,存在。
代码
#include<bits/stdc++.h>
#define rep(i,st,ed) for(int (i)=(st);(i)<=(ed);++(i))
#define bl(u,i) for(int (i)=head[(u)];(i);(i)=e[(i)].nxt)
#define LLM LONG_LONG_MAX
#define LLm LONG_LONG_MIN
#define pii pair<ll,ll>
typedef long long ll;
typedef double db;
using namespace std;
inline void In(ll _,...)
{
va_list lis;
va_start(lis,_);
while(_--)
scanf("%lld",va_arg(lis,ll*));
}
inline void Out(ll _,...)
{
va_list lis;
va_start(lis,_);
while(_--)
printf("%lld\n",va_arg(lis,ll));
}
inline void Out_(ll _,...)
{
va_list lis;
va_start(lis,_);
while(_--)
printf("%lld ",va_arg(lis,ll));
puts("");
}
const int N=1E5+10;
ll n;
ll a[N],b[N];
void solve()
{
In(1,&n);
rep(i,1,n)
In(1,&a[i]);
rep(i,1,n)
In(1,&b[i]);
ll flag=0,end=0;
rep(i,1,n)
{
if(a[i]<b[i])
{
if(end)
{
puts("NO");
return;
}
else if(!flag)
flag=b[i]-a[i];
else if(b[i]-a[i]!=flag)
{
puts("NO");
return;
}
}
else if(a[i]==b[i] && flag)
end=1;
else if(a[i]>b[i])
{
puts("NO");
return;
}
}
puts("YES");
}
int main()
{
int _;
cin>>_;
while(_--)
{
solve();
}
}
B
思路
合法的一天数据总和必定为,考虑维护当天的数据总和,一旦清零就结束这一天。如果为负数或一个元素在一天当中出现超过一次,则视为不合法的一天,用维护元素是否出现过,每一天结束都清空。
这道题刚开始考虑的是一旦遇到一个重复出现的元素就检查是否为,如果为,就结束这一天并以这个重复出现的元素作为新一天的第一个元素,否则即视为不合法。这样其实并不正确,因为如果有机会在这个重复出现元素前面结束前一天,就能够保证这个元素合法地加入新的一天。
尽早的结束一天总是合理的,因为它可以允许后面更多的元素加入到新的一天中而不和前一天的元素发生冲突。
代码
#include<bits/stdc++.h>
#define rep(i,st,ed) for(int (i)=(st);(i)<=(ed);++(i))
#define bl(u,i) for(int (i)=head[(u)];(i);(i)=e[(i)].nxt)
#define LLM LONG_LONG_MAX
#define LLm LONG_LONG_MIN
#define pii pair<ll,ll>
typedef long long ll;
typedef double db;
using namespace std;
inline void In(ll _,...)
{
va_list lis;
va_start(lis,_);
while(_--)
scanf("%lld",va_arg(lis,ll*));
}
inline void Out(ll _,...)
{
va_list lis;
va_start(lis,_);
while(_--)
printf("%lld\n",va_arg(lis,ll));
}
inline void Out_(ll _,...)
{
va_list lis;
va_start(lis,_);
while(_--)
printf("%lld ",va_arg(lis,ll));
}
const int N=1E5+10;
ll n,cnt,sum,lst=1;
ll ans[N];
map<ll,ll> ma;
int main()
{
In(1,&n);
ll flag=1;
rep(i,1,n)
{
ll cur;
In(1,&cur);
sum+=cur;
if(sum==0)
{
ans[++cnt]=i-lst+1;
lst=i+1;
ma.clear();
}
else if(sum<0)
{
flag=0;
break;
}
else if(cur>0)
{
if(ma.find(cur)==ma.end())
ma.insert(pair<ll,ll>(cur,1));
else
{
flag=0;
break;
}
}
else if(cur<0 && ma.find(-cur)==ma.end())
{
flag=0;
break;
}
}
if(!flag || sum)
puts("-1");
else
{
sum=0;
rep(i,1,cnt)
sum+=ans[i];
if(sum!=n)
ans[++cnt]=n-sum;
Out(1,cnt);
rep(i,1,cnt)
Out_(1,ans[i]);
puts("");
}
}
C
思路
感觉比B题要水一点。因为不规定取糖的顺序,因此从小到大取一定是最优的。取完之后怎么分配在哪一天吃呢?代价小的放在后几天吃,代价大的放在前几天吃必然最优,具体证明可以反证。但是每次都从头算肯定会超时,考虑如何优化。
假设我们当前已经知道了第天的代价,现在考虑向第天转移。转移的方案必然是所有糖果向后移一位,给新来的代价最大的糖果腾出第一天的第一个位置。那么每一天的第个糖果都到了后一天,对应地每个延后到下一天的糖果都会再额外贡献一份代价。也就是说当加入第个元素的时候,位置上的元素都会对答案产生贡献,只需要维护一下原数组中每隔位的前缀和即可在的时间内完成转移。
思路
#include<bits/stdc++.h>
#define rep(i,st,ed) for(int (i)=(st);(i)<=(ed);++(i))
#define bl(u,i) for(int (i)=head[(u)];(i);(i)=e[(i)].nxt)
#define LLM LONG_LONG_MAX
#define LLm LONG_LONG_MIN
#define pii pair<ll,ll>
typedef long long ll;
typedef double db;
using namespace std;
inline void In(ll _,...)
{
va_list lis;
va_start(lis,_);
while(_--)
scanf("%lld",va_arg(lis,ll*));
}
inline void Out(ll _,...)
{
va_list lis;
va_start(lis,_);
while(_--)
printf("%lld\n",va_arg(lis,ll));
}
inline void Out_(ll _,...)
{
va_list lis;
va_start(lis,_);
while(_--)
printf("%lld ",va_arg(lis,ll));
}
const int N=2E5+10;
ll n,m,ans;
ll a[N],sum[N];
int main()
{
In(2,&n,&m);
rep(i,1,n)
In(1,&a[i]);
sort(a+1,a+n+1);
rep(i,1,n)
{
if(i-m>=1)
sum[i]=sum[i-m]+a[i];
else
sum[i]=a[i];
}
ll ans=0;
rep(i,1,n)
{
if(i<=m)
{
ans+=a[i];
Out_(1,ans);
}
else
{
ans+=sum[i];
Out_(1,ans);
}
}
}
D
思路
所有联通块内的点在数轴上必须是连续的,首先用带权并查集记录下原图中联通块的信息(父节点和结点个数),然后在数轴上扫一遍,如果两个整数所在的联通块没有相连,且前一个联通块在数轴后面还有结点,就合并这两个联通块(比如7和8不属于同一联通块,但是7和9属于同一联通块,就必须合并8所在的联通块和7、9所在的联通块)。合并时要记得维护结点个数。
代码
#include<bits/stdc++.h>
#define rep(i,st,ed) for(int (i)=(st);(i)<=(ed);++(i))
#define bl(u,i) for(int (i)=head[(u)];(i);(i)=e[(i)].nxt)
#define LLM LONG_LONG_MAX
#define LLm LONG_LONG_MIN
#define pii pair<ll,ll>
typedef long long ll;
typedef double db;
using namespace std;
inline void In(ll _,...)
{
va_list lis;
va_start(lis,_);
while(_--)
scanf("%lld",va_arg(lis,ll*));
}
inline void Out(ll _,...)
{
va_list lis;
va_start(lis,_);
while(_--)
printf("%lld\n",va_arg(lis,ll));
}
inline void Out_(ll _,...)
{
va_list lis;
va_start(lis,_);
while(_--)
printf("%lld ",va_arg(lis,ll));
}
const int N=2E5+10;
ll n,m,tot,ans;
ll head[N],fa[N],vis[N],sum[N];
struct Edge{
ll nxt,to;
}e[2*N];
inline void add_edge(ll u,ll v,int flag)
{
e[++tot].nxt=head[u];
e[tot].to=v;
head[u]=tot;
if(flag)
add_edge(v,u,0);
}
ll find(ll x)
{
return fa[x]==x?x:fa[x]=find(fa[x]);
}
ll dfs(ll u)
{
ll flag=0;
bl(u,i)
{
ll v=e[i].to;
if(vis[v])
continue;
flag=1;
vis[v]=1;
dfs(v);
fa[v]=u;
}
return flag;
}
void print()
{
rep(i,1,n)
Out_(1,sum[i]);
}
int main()
{
In(2,&n,&m);
rep(i,1,m)
{
ll u,v;
In(2,&u,&v);
add_edge(u,v,1);
}
rep(i,1,n)
fa[i]=i;
rep(i,1,n)
{
if(!vis[i])
{
vis[i]=1;
ll tmp=dfs(i);
if(!tmp)
vis[i]=0;
}
}
rep(i,1,n)
sum[find(i)]++;
rep(i,1,n)
{
--sum[find(i)];
if(sum[find(i)] && find(i+1)!=find(i))
{
++ans;
sum[find(i)]+=sum[find(i+1)];
fa[find(i+1)]=i;
}
}
Out(1,ans);
}
E
思路
设为已经覆盖完,继续覆盖到需要花费的代价。
可以通过暴力的扩展覆盖到,因此的初始值为。
除此之外,还可以与后面的区间连接起来,将其作为“跳板”,从而减少代价。因为比较小,可以找到所有左端点位于之后的区间,将其左端点延伸到(显然将向右延申并不如将左端点向左延申更优,因为延伸左端点的同时右端点也向后延伸了),总的代价就是。此时右端点的位置是,两者相加维护答案即可。
另外如果已经被初始的区间覆盖,就不需要任何延伸操作了,此时,即覆盖的区间。此处覆盖指的是当覆盖完之后不需要任何操作就可以直接通过访问来获得更优解,因此的情况并不属于覆盖。另外的情况也是可以直接访问的,详情见代码中的判断条件。
代码
#include<bits/stdc++.h>
#define rep(i,st,ed) for(int (i)=(st);(i)<=(ed);++(i))
#define bl(u,i) for(int (i)=head[(u)];(i);(i)=e[(i)].nxt)
#define LLM LONG_LONG_MAX
#define LLm LONG_LONG_MIN
#define pii pair<ll,ll>
typedef long long ll;
typedef double db;
using namespace std;
inline void In(ll _,...)
{
va_list lis;
va_start(lis,_);
while(_--)
scanf("%lld",va_arg(lis,ll*));
}
inline void Out(ll _,...)
{
va_list lis;
va_start(lis,_);
while(_--)
printf("%lld\n",va_arg(lis,ll));
}
inline void Out_(ll _,...)
{
va_list lis;
va_start(lis,_);
while(_--)
printf("%lld ",va_arg(lis,ll));
puts("");
}
struct Node{
ll x,s,l,r;
bool operator < (const Node &tmp) const
{
return l>tmp.l;
}
}a[85];
ll n,m;
ll f[(ll)1E5+10];
int main()
{
In(2,&n,&m);
rep(i,1,n)
{
In(2,&a[i].x,&a[i].s);
a[i].l=max(0ll,a[i].x-a[i].s);
a[i].r=min(m,a[i].x+a[i].s);
}
sort(a+1,a+n+1);
for(int j=m;j>=0;--j)
{
f[j]=m-j;
rep(i,1,n)
{
if(a[i].l<=j+1 && a[i].r>=j+1)
{
f[j]=f[a[i].r];
break;
}
if(j<a[i].l)
{
ll dis=a[i].l-j-1;
f[j]=min(f[j],dis+f[min(m,a[i].r+dis)]);
}
}
}
Out(1,f[0]);
}
F
思路
这题太牛逼了,先放代码
代码
#include<bits/stdc++.h>
#define rep(i,st,ed) for(int (i)=(st);(i)<=(ed);++(i))
#define bl(u,i) for(int (i)=head[(u)];(i);(i)=e[(i)].nxt)
#define LLM LONG_LONG_MAX
#define LLm LONG_LONG_MIN
#define pii pair<ll,ll>
typedef long long ll;
typedef double db;
using namespace std;
inline void In(ll _,...)
{
va_list lis;
va_start(lis,_);
while(_--)
scanf("%lld",va_arg(lis,ll*));
}
inline void Out(ll _,...)
{
va_list lis;
va_start(lis,_);
while(_--)
printf("%lld\n",va_arg(lis,ll));
}
inline void Out_(ll _,...)
{
va_list lis;
va_start(lis,_);
while(_--)
printf("%lld ",va_arg(lis,ll));
}
const int N=6E5+10;
ll n,m,k,t,tot;
ll fa[N],head[N],vis[N],dis[N],f[N][24],p[N][24],dep[N];
struct Node{
ll u,d;
bool operator < (const Node &tmp) const
{
return d>tmp.d;
}
};
struct Edge{
ll frm,nxt,to,w;
bool operator < (const Edge &tmp) const
{
return w<tmp.w;
}
}e[N];
priority_queue<Node> q;
inline void add_edge(ll u,ll v,ll w,int flag)
{
e[++tot].nxt=head[u];
e[tot].frm=u;
e[tot].to=v;
e[tot].w=w;
head[u]=tot;
if(flag)
add_edge(v,u,w,0);
}
void Dij()
{
rep(i,1,n)
{
if(i>k)
dis[i]=LLM>>1;
else
q.push((Node){i,0});
}
while(!q.empty())
{
Node cur=q.top();
q.pop();
ll u=cur.u;
if(vis[u])
continue;
vis[u]=1;
bl(u,i)
{
ll v=e[i].to;
if(dis[v]>dis[u]+e[i].w)
q.push((Node){v,dis[v]=dis[u]+e[i].w});
}
}
}
ll find(ll x)
{
return fa[x]==x?x:fa[x]=find(fa[x]);
}
void print()
{
rep(i,1,n)
Out(1,dep[i]);
}
void Kru()
{
rep(i,1,tot)
e[i].w+=dis[e[i].frm]+dis[e[i].to];
rep(i,1,n)
{
head[i]=0;
fa[i]=i;
}
stable_sort(e+1,e+tot+1);
ll cnt=0;
ll tot_=tot;
tot=0;
rep(i,1,tot_)
{
ll u=e[i].frm,v=e[i].to;
ll uu=find(u),vv=find(v);
if(uu==vv)
continue;
fa[uu]=vv;
++cnt;
add_edge(u,v,e[i].w,1);
if(cnt==n-1)
break;
}
}
void dfs(ll u,ll father)
{
dep[u]=dep[father]+1;
rep(i,0,22)
{
p[u][i+1]=p[p[u][i]][i];
f[u][i+1]=max(f[u][i],f[p[u][i]][i]);
}
bl(u,i)
{
ll v=e[i].to;
if(v==father)
continue;
p[v][0]=u;
f[v][0]=e[i].w;
dfs(v,u);
}
}
ll lca(ll x,ll y)
{
ll ret=LLm;
if(dep[x]<dep[y])
swap(x,y);
for(int i=22;i>=0;--i)
{
if(dep[p[x][i]]>=dep[y])
{
ret=max(ret,f[x][i]);
x=p[x][i];
}
if(x==y)
return ret;
}
for(int i=22;i>=0;--i)
{
if(p[x][i]!=p[y][i])
{
ret=max(ret,f[x][i]);
ret=max(ret,f[y][i]);
x=p[x][i];
y=p[y][i];
}
}
return max(ret,max(f[x][0],f[y][0]));
}
int main()
{
In(4,&n,&m,&k,&t);
rep(i,1,m)
{
ll u,v,w;
In(3,&u,&v,&w);
add_edge(u,v,w,1);
}
Dij();
Kru();
dfs(1,0);
while(t--)
{
ll x,y;
In(2,&x,&y);
Out(1,lca(x,y));
}
}