目录
题目地址:https://codeforces.com/contest/1253
A Single Push
题意:有两个一样长的数组a、b,问是否可以给a的某一段连续区间加上一个正整数(或者不进行操作),使得a与b相等。
思路:a与b按位作差,如果a最多存在一段连续的负数,则可以;否则不行。
代码:
const int maxn=1e5+5;
int a[maxn],n;
int main()
{
//freopen("input.txt","r",stdin);
int T=read();
while(T--)
{
int n=read();
REP(i,1,n) a[i]=read();
REP(i,1,n) a[i]-=read();
int i=1,temp=0;
while(i<=n && !a[i]) i++;
if(i<=n) temp=a[i];
while(i<=n && a[i]<0 && a[i]==temp) i++;
while(i<=n && !a[i]) i++;
puts(i<=n?"NO":"YES");
}
return 0;
}
B Silly Mistake
题意:一个人进入公司的事件记为 id,出公司记为 -id,给定一个事件表,问该时间表是否可以划分为合理的若干天,如果可以输出天数以及每一天的事件数;否则输出-1 (如果某一天合理,则:每个人只能进入一次、人不能没有进入就出去、一天结束时公司为空)
思路:
代码:
int tot=0,ans[100005];
int main()
{
//freopen("input.txt","r",stdin);
int n=read();
set<int> s,t;
REP(i,1,n)
{
int x=read();
if(x>0)
{
if(t.count(x))
{
if(!s.empty()) return puts("-1"),0;
ans[tot++]=t.size()*2;
t.clear();
}
else s.insert(x),t.insert(x);
}
else
{
x=-x;
if(!s.count(x)) return puts("-1"),0;
s.erase(x);
if(s.empty()) ans[tot++]=t.size()*2,t.clear();
}
}
if(!s.empty()) return puts("-1"),0;
printf("%d\n",tot);
REP(i,0,tot-1) printf("%d ",ans[i]);
return 0;
}
C Sweets Eating
题意:
思路:按照模m的余数分组进行递推就很好做了。
代码:
const int maxn=2e5+5;
LL f[maxn],s[maxn];
int main()
{
//freopen("input.txt","r",stdin);
int n=read(),m=read();
REP(i,1,n) s[i]=read();
sort(s+1,s+n+1);
REP(i,1,n) s[i]+=s[i-1];
REP(i,1,m) f[i]=s[i];
REP(i,1,m) for(int j=i+m;j<=n;j+=m) f[j]=f[j-m]+s[j];
REP(i,1,n) printf("%lld ",f[i]);
return 0;
}
D Harmonious Graph
题意:给定一个无向图,问至少需要添加多少条边,使得该无向图的每个分支结点集的编号是连续的。
思路:用并查集记录联通信息,同时记录每个父结点所在集合的大小,当合并时优先合并到编号小的结点上,然后用自动机的思路顺序匹配,在匹配的过程中如果当前结点的父结点是一个新结点,而上一个父结点的匹配数目不足其集合大小时(说明后面还有结点能够匹配到上一个父结点),把当前结点与上一个父结点集合合并。
代码:
const int maxn=2e5+5;
int far[maxn],n,m,num[maxn];
int findd(int x){return x==far[x]?x:far[x]=findd(far[x]);}
void unite(int x,int y)
{
int fx=findd(x),fy=findd(y);
if(fx==fy) return;
if(fx<fy) num[fx]+=num[fy],far[fy]=fx;
else num[fy]+=num[fx],far[fx]=fy;
}
int main()
{
//freopen("input.txt","r",stdin);
n=read(),m=read();
REP(i,1,n) far[i]=i,num[i]=1;
while(m--)
{
int u=read(),v=read();
unite(u,v);
}
int ans=0,pipei=0,temp=0,i=1;
while(i<=n)
{
if(!temp) temp=findd(i),pipei=0;
else if(findd(i)==temp) pipei++,i++;
else if(pipei<num[temp]) unite(temp,i),ans++;
else temp=0;
}
cout<<ans;
return 0;
}
E Antenna Coverage
题意:有若干个(n个)天线覆盖范围为 ,每花费 1 可以使其中一个天线覆盖范围 +1( 加 1),问使得所有天线能够覆盖 的最少花费是多少。( )
思路:dp。设 f[i] 表示覆盖 [1, i] 的最少花费,对于当前的 i,如果 i 在任意一个区间内,则 f[i] = f[i-1] ;否则,枚举所有区间:首先一定有 ,也即直接把 i-1 的那个区间扩展即可,然后枚举时如果 ,用 来更新答案;如果 ,用 来更新答案。
代码:
const int maxn=1e5+5;
int n,m,s[maxn],x[maxn],f[maxn];
int main()
{
//freopen("input.txt","r",stdin);
n=read(),m=read();
REP(i,1,n) x[i]=read(),s[i]=read();
REP(i,1,m)
{
int flag=0,minx=f[i-1]+1;
REP(j,1,n)
{
if(i>=x[j]-s[j] && i<=x[j]+s[j]) {flag=1; break;}
else if(x[j]<i) minx=min(minx,f[max(2*x[j]-i-1,0)]+i-x[j]-s[j]);
else minx=min(minx,f[i-1]+x[j]-s[j]-1);
}
if(flag) f[i]=f[i-1];
else f[i]=minx;
}
cout<<f[m];
return 0;
}
F Cheap Robot
题意:一个n个结点,m条边的带边权简单联通无向图,其中1-k号结点为central结点。机器人在central结点可以充满电,而每经过一条边电量会减少边权值。有q组询问,每组询问查询从结点u到结点v(保证u,v为central,即一开始电量是满的),能够到达的最少电量容量c是多少。n,m,q都是1e5级别的。
思路:先用Dijkstra处理数组dis,其意义为dis[i]表示结点i到某一个central的最短距离。假设机器人当前在结点i,剩余电量为x,那么必定成立 ,左边不等式是因为目的结点为central结点,右边不等式成立是因为起始结点为central结点。换句话说,只要满足 ,我们在i结点处就可以让剩余电量取得最大值 ,因为可以跑到最近的central去充满电然后再跑回来。我们考虑机器人从u到v经过一条边(u, v, w),在u处的剩余电量为 c-dis[u],所以要满足 ,即 。
那么我们就可以把 dis[u]+dis[v]+w 看成某一条边的边权,所以等价于要使得无向图两点之间最大边权最小,用 Kruskal重构树+树链剖分+LCA就可以求解。(类似于 货车运输 这道题,这道题是要使两点之间最小边权最大)
代码:
const int maxn=2e5+5;
const LL inf=1ll<<50;
struct edge {int u,v,w;} e[maxn<<1];
vector<int> G[maxn];
vector<edge> GG[maxn];
int n,k,m,q,root,cnt,far[maxn];
LL a[maxn],dis[maxn];
int siz[maxn],f[maxn],d[maxn],son[maxn],dfn[maxn],which[maxn],top[maxn];
bool cmp(edge x,edge y) {return dis[x.u]+dis[x.v]+x.w<dis[y.u]+dis[y.v]+y.w;}
int findd(int x){return x==far[x]?x:far[x]=findd(far[x]);}
void dfs1(int u,int fa,int depth)
{
f[u]=fa; d[u]=depth; siz[u]=1; son[u]=0;
REP(i,0,G[u].size()-1)
{
int v=G[u][i];
if(v==fa) continue;
dfs1(v,u,depth+1);
siz[u]+=siz[v];
if(siz[v]>siz[son[u]]) son[u]=v;
}
}
void dfs2(int u,int tf)
{
top[u]=tf; dfn[u]=++cnt; which[cnt]=u;
if(!son[u]) return;
dfs2(son[u],tf);
REP(i,0,G[u].size()-1)
{
int v=G[u][i];
if(v!=f[u] && v!=son[u]) dfs2(v,v);
}
}
int LCA(int x,int y)
{
while(top[x]!=top[y])
d[top[x]]>d[top[y]]?(x=f[top[x]]):(y=f[top[y]]);
return d[x]<d[y]?x:y;
}
bool vis[maxn];
void get_dis()
{
typedef pair<LL,int> P;
priority_queue<P,vector<P>,greater<P> > que;
REP(i,1,k) que.push(P(dis[i],i));
REP(i,k+1,n) dis[i]=inf;
while(!que.empty())
{
P p=que.top(); que.pop();
int d=p.first,u=p.second;
if(vis[u]) continue;
vis[u]=1;
REP(i,0,GG[u].size()-1)
{
int v=GG[u][i].v,w=GG[u][i].w;
if(vis[v]) continue;
if(dis[v]>dis[u]+w)
{
dis[v]=dis[u]+w;
que.push(P(dis[v],v));
}
}
}
}
void add_edge(int u,int v) {G[u].push_back(v); G[v].push_back(u);}
void Kruskal()
{
sort(e+1,e+m+1,cmp);
REP(i,1,m)
{
int u=e[i].u,v=e[i].v;
int fu=findd(u),fv=findd(v);
if(fu==fv) continue;
a[++n]=dis[u]+dis[v]+e[i].w;
far[fu]=n; far[fv]=n; far[n]=n;
add_edge(n,fu); add_edge(n,fv);
}
}
int main()
{
n=read(),m=read(),k=read(),q=read();
REP(i,1,m)
{
int u=read(),v=read(),w=read();
e[i]=(edge){u,v,w};
GG[u].push_back((edge){u,v,w});
GG[v].push_back((edge){v,u,w});
}
REP(i,1,n) far[i]=i; // union-find set
get_dis();
Kruskal();
dfs1(n,0,0);
dfs2(n,n);
while(q--)
{
int u=read(),v=read();
printf("%lld\n",a[LCA(u,v)]);
}
return 0;
}