Codeforces Round #703 (Div. 2)
Codeforces Round #703 (Div. 2)
掉分场QAQ。
A. Shifting Stacks
题意:最开始有\(n\)个数,每次可以指定一个位置\(h_i>0\)且\(i<n\)的位置\(i\),让\(h_i-1\)并让\(h_{i+1}+1\),问能否通过操作使得\(h\)单调递增。
因为只要单调递增即可,因此我们可以只在第\(i\)个位置上保留\(i-1\)即可,把剩下多出来的无脑堆到后边去,如果发现\(i\)这个位置达不到\(i-1\)那么就无解,否则一定有解。
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 200005
#define int long long
using namespace std;
int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*f;
}
int T,n,a[N];
signed main()
{
T=read();
while(T--)
{
n=read();
for(int i=1;i<=n;i++)a[i]=read();
int ff=1;
for(int i=1;i<=n;i++)
{
if(a[i]<i-1)ff=0;
a[i+1]+=(a[i]-(i-1));
}
if(ff)puts("YES");
else puts("NO");
}
return 0;
}
B. Eastern Exhibition
这B题就是来搞心态的
题意:问有多少个整点能使得给出的\(n\)个点到这个点的曼哈顿距离和最小。
曼哈顿距离\(x,y\)是相互独立的,那么我们考虑一个点\((a,b)\),假设我们按\(x\)排序后,\(x_p\leq a\leq x_{p+1}\),按\(y\)排序后\(y_q\leq b\leq y_{q+1}\),那么距离和即为
注意到这实际上就是两个数轴上,到n个点距离和最小的式子,因此如果\(n\)为奇数直接取中位数即可,答案即为\(1\),如果\(n\)为偶数那么一定是取\(n/2\)到\(n/2+1\)这段区间内的数,\(x\)轴\(y\)轴的范围乘起来即可。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 200005
#define int long long
using namespace std;
int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*f;
}
int T,n,x[N],y[N];
signed main()
{
T=read();
while(T--)
{
n=read();
for(int i=1;i<=n;i++)x[i]=read(),y[i]=read();
sort(x+1,x+1+n);sort(y+1,y+1+n);
if(n%2)puts("1");
else printf("%lld\n",(x[n/2+1]-x[n/2]+1)*(y[n/2+1]-y[n/2]+1));
}
}
C. Guessing the Greatest
题意:交互题,每次可以询问一段区间内的次大值的位置,问整个序列最大值的位置。最多可问\(40(C1)\)/\(20(C2)\)次。
我们先考虑一个全局次大值\(x\),我们考虑全局最大值的位置在它的左边(右边是对称的),那么一定是这样的,设全局最大值位置为\(y\),那么一定有当\(a\leq y\)时\(query(a,x)=x\),否则\(query(a,x)\neq x\)。
有了这个性质于是就可以二分了,询问次数是\(\log\)级别的,可以在\(20\)次之内求出答案。
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 200005
using namespace std;
int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*f;
}
int n,cnt;
int query(int l,int r)
{
if(l==r)return 0;
printf("? %d %d\n",l,r);
fflush(stdout);
int res;
scanf("%d",&res);
return res;
}
int main()
{
scanf("%d",&n);
int l=1,r=n,now=query(1,n);
while(l<r)
{
int mid=(l+r)/2;
if(now<=mid)
{
if(query(1,mid)==now)r=mid;
else l=mid+1;
}
else
{
if(query(mid+1,n)==now)l=mid+1;
else r=mid;
}
}
printf("! %d\n",l);
fflush(stdout);return 0;
}
D. Max Median
题意:求长度不小于\(k\)的中位数最大的子串,求中位数。
考虑二分答案\(x\),那么对于一个字串,一定是大于等于\(x\)的数越多越好,且来一个大于等于\(x\)的数可以和一个小于\(x\)的数的贡献抵消,因此将大于等于\(x\)的数的贡献设为\(1\),否则为\(-1\),那么问题就变成了求一个区间和大于\(0\)的长度不小于\(k\)的子段,前缀和即可。
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 200005
using namespace std;
int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*f;
}
int n,k,a[N],s[N],minn[N];
int check(int x)
{
for(int i=1;i<=n;i++)s[i]=s[i-1]+((a[i]>=x)?1:-1);
for(int i=1;i<=n;i++)minn[i]=min(minn[i-1],s[i]);
int res=-1e9;
for(int i=k;i<=n;i++)res=max(res,s[i]-minn[i-k]);
return res>0;
}
int main()
{
n=read();k=read();
for(int i=1;i<=n;i++)a[i]=read();
int l=0,r=1e9;
while(l<=r)
{
int mid=(l+r)>>1;
if(check(mid))l=mid+1;
else r=mid-1;
}
printf("%d\n",l-1);
}
E. Paired Payment
题意:给一张带权无向图,如果原图上有两条边\((a,b,c)\)和\((b,d,e)\),那么你可以从\(a\)走到\(d\),花费为\((c+e)^2\),边权最多\(50\)。
考虑暴力拆点,把一个点拆为一个能正常走到的点和\(50\)个中转点,那么对于一个状态\(sta(i,c)\),表示\(i\)号点状态为\(c\),如果\(c=0\)那么为正常走到的点,否则表示通过一条边权为\(c\)的点走过来的状态。
那么设\(dis(i,j)\)表示走到\(sta(i,j)\)的最短路,转移的时候看是否是中转点,即\(j\)是否等于\(0\),如果是正常点就转移到下一个点的中转状态,否则转移到下一个点的正常状态,再走到正常状态的时候结算一下这两条边的贡献即可。
直接大力\(dij\)即可。
#pragma GCC optimize(3,"Ofast","inline")
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define N 400005
#define mp make_pair
#define P pair<int,pair<int,int> >
using namespace std;
int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*f;
}
int n,m,v[N],w[N],head[N],nxt[N],cnt,f[N/2][51],vis[N/2][51];
void add(int a,int b,int c)
{
v[++cnt]=b;
w[cnt]=c;
nxt[cnt]=head[a];
head[a]=cnt;
}
void dij()
{
memset(f,0x3f,sizeof(f));
priority_queue<P,vector<P>,greater<P> >q;
q.push(mp(0,mp(1,0)));
f[1][0]=0;
while(!q.empty())
{
pair<int,int>c=q.top().second;
q.pop();int x=c.first,y=c.second;
if(vis[x][y])continue;
for(int i=head[x];i;i=nxt[i])
{
if(y==0)
{
if(f[v[i]][w[i]]>f[x][y])
{
f[v[i]][w[i]]=f[x][y];
q.push(mp(f[v[i]][w[i]],mp(v[i],w[i])));
}
}
else
{
if(f[v[i]][0]>f[x][y]+(y+w[i])*(y+w[i]))
{
f[v[i]][0]=f[x][y]+(y+w[i])*(y+w[i]);
q.push(mp(f[v[i]][0],mp(v[i],0)));
}
}
}
}
}
int main()
{
n=read();m=read();
for(int i=1;i<=m;i++)
{
int x=read(),y=read(),z=read();
add(x,y,z);add(y,x,z);
}
dij();
for(int i=1;i<=n;i++)
{
if(f[i][0]==0x3f3f3f3f)printf("-1 ");
else printf("%d ",f[i][0]);
}
return 0;
}
F. Pairs of Paths
题意:给一棵\(n\)个结点的树和\(m\)条路径,问有多少对路径相交部分有些只有一个顶点。
满足题意的两条路径的交点必然是其中某条路径的端点的\(lca\)。
不然的话从这个交点往上跳一步还是交点,因此不符合要求。
那么就有这两种情况
要么是LCA在同一处,要么是在不同处。
先考虑在同一处的情况,对于每一个点我们计算LCA为当前点的答案,那么我们只需要顺序扫过去,记录每个点所在的子树,然后开个桶减掉不合法的即可。具体来说就记\(b_i\)为\(i\)号子树中有多少条链,我们先让\(x < y\),(x和y是两个端点所在的子树),然后按\(x\)排序,我们只需要每次加前边的链数之后减去\(b[y]\)即可。
不在同一处的情况比较简单,就直接统计一颗子树内有多少个端点即可,按照\(LCA\)的深度排序后可以用树状数组维护,注意要减掉来自这个链两端所在子树的贡献。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 600005
#define lowbit(x) x&-x
#define mp make_pair
#define int long long
using namespace std;
int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*f;
}
int n,m,tot,v[N],head[N],nxt[N],cnt,dfn[N],dfstime,fa[N][25],dep[N],rfn[N],b[N],ans;
struct node
{
int lca,a,b,x,y;
}q[N];
struct BIT
{
int c[N];
void modify(int x,int k){for(;x<=n;x+=lowbit(x))c[x]+=k;}
int query(int x){int res=0;for(;x;x-=lowbit(x))res+=c[x];return res;}
}t;
void add(int a,int b)
{
v[++cnt]=b;
nxt[cnt]=head[a];
head[a]=cnt;
}
void dfs(int x,int fath)
{
dep[x]=dep[fath]+1;dfn[x]=++dfstime;
for(int i=0;i<=19;i++)fa[x][i+1]=fa[fa[x][i]][i];
for(int i=head[x];i;i=nxt[i])
{
if(v[i]==fath)continue;
fa[v[i]][0]=x;
dfs(v[i],x);
}
rfn[x]=dfstime;
}
pair<int,int> lca(int x,int y)
{
if(x==y)return mp(-1,-1);
int ff=0;
if(dep[x]<dep[y])swap(x,y),ff=1;
int X=x;
for(int i=20;i>=0;i--)
{
if(dep[fa[x][i]]>=dep[y])x=fa[x][i];
}
if(x==y)
{
x=X;
int D=dep[x]-dep[y]-1;
//cout<<x<<" "<<D<<endl;
for(int i=20;i>=0;i--)if(D>=(1<<i))D-=(1<<i),x=fa[x][i];
if(ff)return mp(-1,x);
else return mp(x,-1);
}
for(int i=20;i>=0;i--)
{
if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
}
if(ff)swap(x,y);
return mp(x,y);
}
int cmp(node a,node b)
{
if(dep[a.lca]!=dep[b.lca])return dep[a.lca]<dep[b.lca];
if(a.lca!=b.lca)return a.lca<b.lca;
if(a.a!=b.a)return a.a>b.a;
return a.b>b.b;
}
signed main()
{
n=read();tot=n;
for(int i=1;i<n;i++)
{
int x=read(),y=read();
add(x,y);add(y,x);
}
dfs(1,0);m=read();
//cout<<"!!!"<<fa[3][0]<<endl;
for(int i=1;i<=m;i++)
{
q[i].x=read();q[i].y=read();
pair<int,int>tmp=lca(q[i].x,q[i].y);
q[i].a=((tmp.first==-1)?(++tot):tmp.first);
q[i].b=((tmp.second==-1)?(++tot):tmp.second);
if(tmp.first!=-1&&tmp.second!=-1)q[i].lca=fa[tmp.first][0];
else if(max(tmp.first,tmp.second)>0)q[i].lca=fa[max(tmp.first,tmp.second)][0];
else q[i].lca=q[i].x;
//cout<<"LCA="<<q[i].lca<<" "<<tmp.first<<" "<<tmp.second<<endl;;
if(q[i].a>q[i].b)swap(q[i].a,q[i].b),swap(q[i].x,q[i].y);
}
sort(q+1,q+1+m,cmp);
//for(int i=1;i<=m;i++)cout<<q[i].x<<" "<<q[i].y<<" "<<q[i].a<<" "<<q[i].b<<" "<<q[i].lca<<endl;
//puts("");
for(int i=1;i<=m;i++)
{
int j=i,sum=0;while(j<m&&q[j+1].lca==q[j].lca)j++;
for(int k=i;k<=j;k++)
{
int L=k;while(L<j&&q[L+1].a==q[k].a)L++;
for(int p=k;p<=L;p++)ans+=sum-b[q[p].b];
for(int p=k;p<=L;p++)b[q[p].a]++,b[q[p].b]++;
sum+=L-k+1;
k=L;
}
for(int k=i;k<=j;k++)
{
ans+=t.query(rfn[q[k].lca])-t.query(dfn[q[k].lca]-1);
//cout<<ans<<endl;
if(q[k].a<=n)ans-=t.query(rfn[q[k].a])-t.query(dfn[q[k].a]-1);
if(q[k].b<=n)ans-=t.query(rfn[q[k].b])-t.query(dfn[q[k].b]-1);
//cout<<t.query(rfn[q[k].a])-t.query(dfn[q[k].a]-1)<<endl;
}
for(int k=i;k<=j;k++)t.modify(dfn[q[k].x],1),t.modify(dfn[q[k].y],1);
i=j;
}
printf("%lld\n",ans);
}