昨天毒奶了一口比 hzr 少一题,今天打完发现和 hzr 题数相等,结果 fst 了(
057 uoj#747. 【UNR #6】面基之路
一开始猜了个结论,后来发现是极为显然的。
一定存在一种最优方案使得所有人最后在同一位置,因为见面了之后可以让网友一直跟着 hehe 蚤到最后面基。
于是枚举一下在哪个位置面基,点上很容易考虑,边上需要考虑哪些网友从 \(x\) 进入,哪些网友从 \(y\) 进入,答案是一个关于从 \(x\) 进入时间最大值,从 \(y\) 进入时间最大值的式子,枚举一下就好了。
复杂度 \(O(nk\log n+mk\log k)\)。
比赛的时候和 myy 想的一样,以为贪心地将离 \(x\) 近的分配给 \(x\) 是对的,拍了一千多组没过就丢一边了。。。
#include<stdio.h>
#include<queue>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn=100005,maxm=200005,maxk=25;
const long long inf=1e18;
int n,m,k;
long long ans;
long long dis[maxk][maxn];
int p[maxk],vis[maxn],xx[maxm],yy[maxm],zz[maxm];
vector<int>v[maxn],w[maxn];
priority_queue< pair<long long,int> >q;
void dijkstra(int id,int s){
for(int i=1;i<=n;i++)
dis[id][i]=inf,vis[i]=0;
dis[id][s]=0,q.push(make_pair(0,s));
while(!q.empty()){
int x=q.top().second;
q.pop();
if(vis[x])
continue;
vis[x]=1;
for(int i=0;i<v[x].size();i++){
int y=v[x][i],z=w[x][i];
if(dis[id][y]>dis[id][x]+z)
dis[id][y]=dis[id][x]+z,q.push(make_pair(-dis[id][y],y));
}
}
}
struct limit{
long long l,r;
}t[maxk];
inline int cmp(limit a,limit b){
return a.l>b.l||(a.l==b.l&&a.r<b.r);
}
inline void chk(long long mx0,long long mx1,int z){
long long v=min(max(0ll,mx1-mx0+z),0ll+z+z);
ans=min(ans,max(mx0+mx0+v,mx1+mx1+z+z-v));
}
int main(){
// freopen("A.in","r",stdin);
// freopen("A.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1,x,y,z;i<=m;i++){
scanf("%d%d%d",&x,&y,&z),v[x].push_back(y),v[y].push_back(x);
w[x].push_back(z),w[y].push_back(z),xx[i]=x,yy[i]=y,zz[i]=z;
}
scanf("%d",&k),dijkstra(0,1);
for(int i=1;i<=k;i++)
scanf("%d",&p[i]),dijkstra(i,p[i]);
ans=1e18;
for(int i=1;i<=n;i++){
long long res=0;
for(int j=0;j<=k;j++)
res=max(res,dis[j][i]);
ans=min(ans,res+res);
}
for(int i=1;i<=m;i++){
int x=xx[i],y=yy[i],z=zz[i];
for(int j=0;j<=k;j++)
t[j]=limit{dis[j][x],dis[j][y]};
sort(t,t+1+k,cmp);
long long mx=0;
for(int j=0;j<=k;j++)
chk(t[j].l,mx,z),mx=max(mx,t[j].r);
chk(0,mx,z);
}
printf("%lld\n",ans);
return 0;
}
058 uoj#748. 【UNR #6】机器人表演
抱着一个假做法写了好久,最后发现怎么计数都会重。重构做法之后编了好久才过。。。
而此时 hzr 早就过了,orz hzr。
首先给出合法串的判定方式:存在去掉一个为 \(S\) 子序列的方式,使得剩下的串将 \(0,1\) 分别视作 \((,)\) 后为合法括号串。
一个很显然的错误想法是钦定去掉最靠前的匹配子序列。给出一组 hack:0010
。(原串为 00
)
我们考虑在 dp 过程中修正这一错误,当我们加入一个 1
使得括号序列不合法时,我们将当前匹配子序列最靠后的和为 \(1\) 的后缀补偿给括号序列,预处理这个位置即可做到 \(O(nt(n+t))\)。
其实我也不会很严谨地证明这一过程,但是可以发现每次补偿完,前缀和都会到达 \(0\),因此我们钦定的子序列是下标对应子序列字典序最小的子序列,不重不漏。
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=305,mod=998244353;
int n,t,ans;
int f[maxn][maxn],g[maxn][maxn],pre[maxn];
string s;
inline int inc(int x){
return x>=mod? x-mod:x;
}
int main(){
// freopen("B.in","r",stdin);
// freopen("B.out","w",stdout);
scanf("%d%d",&n,&t),cin>>s,s=s+" ";
pre[0]=-1;
for(int i=1;i<=n;i++){
int cnt=0;
pre[i]=-1;
for(int j=i-1;j>=0;j--){
cnt+=s[j]=='0'? 1:-1;
if(cnt==1){
pre[i]=j;
break;
}
}
}
f[0][0]=1;
for(int i=0;i<n+t+t;i++){
for(int j=0;j<=n;j++)
for(int k=0;k<=t;k++)
if(f[j][k]){
// printf("i=%d j=%d k=%d p=%d\n",i,j,k,p);
int p=f[j][k];
if(j<n)
g[j+1][k]=inc(g[j+1][k]+p);
if(s[j]!='0')
g[j][k+1]=inc(g[j][k+1]+p);
if(s[j]!='1'){
if(k>0)
g[j][k-1]=inc(g[j][k-1]+p);
else if(pre[j]!=-1)
g[pre[j]][0]=inc(g[pre[j]][0]+p);
}
}
for(int j=0;j<=n;j++)
for(int k=0;k<=t;k++)
f[j][k]=g[j][k],g[j][k]=0;
}
printf("%d\n",f[n][0]);
return 0;
}
059 uoj#749. 【UNR #6】稳健型选手
应该做出来的,可惜了。
一个比较简单的想法是反悔贪心,从左到右,奇数位置加入堆,然后每个位置检查是否比能替代堆中的最小值。
这一贪心也可以类似地做到从右到左,奇数位置将堆中的最大值计入答案就好了。
对询问猫树分治,问题在于二区间合并,方案的变化实际上是很好刻画的,无非是从左区间取出 \(k\) 个选了的位置来换右区间 \(k\) 个没选的位置。
主席树上二分即可,复杂度 \(O(n\log^2 n+q\log n)\)。
#include<stdio.h>
#include<vector>
#include<queue>
using namespace std;
const int maxn=200005,maxt=maxn*60,V=1000000000;
int n,m,tot;
int a[maxn],id[maxn],qx[maxn],qy[maxn],tmp[maxn],rt[maxn],lc[maxt],rc[maxt],sz[maxt];
long long ans[maxn],sum[maxt];
vector<int>v[maxn];
priority_queue<int>q;
void modify(int l,int r,int &now,int p){
tot++,lc[tot]=lc[now],rc[tot]=rc[now],sz[tot]=sz[now]+1,sum[tot]=sum[now]+p,now=tot;
if(l==r)
return ;
int mid=(l+r)>>1;
if(p<=mid)
modify(l,mid,lc[now],p);
else modify(mid+1,r,rc[now],p);
}
long long query(int l,int r,int a,int b,int u,int v){
if(l==r)
return 1ll*(u-v)*l;
int mid=(l+r)>>1,nu=u+sz[lc[a]],nv=v+sz[rc[b]];
if(nu<nv)
return query(mid+1,r,rc[a],rc[b],nu,v)-sum[lc[a]];
if(nu>nv)
return query(l,mid,lc[a],lc[b],u,nv)+sum[rc[b]];
return sum[rc[b]]-sum[lc[a]];
}
void solve(int l,int r,int ql,int qr){
if(l==r){
for(int i=ql;i<=qr;i++)
ans[id[i]]=a[l];
return ;
}
int mid=(l+r)>>1,nql=ql-1,nqr=qr+1;
for(int i=mid+1;i<=r;i++){
vector<int>_;
_.swap(v[i]);
}
for(int i=ql;i<=qr;i++){
if(qy[id[i]]<=mid)
tmp[++nql]=id[i];
else if(qx[id[i]]>mid)
tmp[--nqr]=id[i];
else v[qy[id[i]]].push_back(id[i]);
}
for(int i=ql;i<=qr;i++)
id[i]=tmp[i];
for(int o=0;o<=1;o++){
tot=lc[0]=rc[0]=sum[0]=sz[0]=0;
while(!q.empty())
q.pop();
for(int i=mid;i>=l;i--){
rt[i]=i==mid? 0:rt[i+1],q.push(a[i]);
if((i&1)==o)
modify(1,V,rt[i],q.top()),q.pop();
}
while(!q.empty())
q.pop();
long long now=0;
for(int i=mid+1;i<=r;i++){
rt[i]=i==mid+1? 0:rt[i-1],now+=a[i],q.push(-a[i]);
if((i&1)!=o)
now+=q.top(),modify(1,V,rt[i],-q.top()),q.pop();
for(int j=0;j<v[i].size();j++){
int k=v[i][j],x=qx[k],y=qy[k];
if((x&1)==o)
ans[k]=sum[rt[x]]+now+query(1,V,rt[x],rt[i],0,0);
}
}
}
if(ql<=nql)
solve(l,mid,ql,nql);
if(nqr<=qr)
solve(mid+1,r,nqr,qr);
}
int main(){
// freopen("C.in","r",stdin);
// freopen("C.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<=m;i++)
scanf("%d%d",&qx[i],&qy[i]),id[i]=i;
solve(1,n,1,m);
for(int i=1;i<=m;i++)
printf("%lld\n",ans[i]);
return 0;
}