2019.3.18考试&2019.3.19考试&2019.3.21考试

时间:2023-03-09 08:42:32
2019.3.18考试&2019.3.19考试&2019.3.21考试

2019.3.18

C O D E

T1

树上直接贪心,环上for一遍贪心


哇说的简单,码了将近一下午终于码出来了

感觉自己码力/写题策略太糟糕了,先是搞了一个细节太多的写法最后不得不弃疗了,然后第二次思路又有问题,最后重构了两遍代码

大概先是需要多想,想清楚了不要先考虑细节,果断写+调

废话结束


对于入度大于一且不在环上的点直接贪心留最大的

对于一个完美无瑕的环直接断最小的(指没有被环以外的点指着)

对于入度大于一且在环上的点,先假装它就是普通的入度大于一的点来做并记录每个点是否断了环上的边和断的边中的最大值;最后把没有删边的不完美的环每个单独拿出来,枚举到底断那一条边修正答案

细节不说了,这种题的细节也没啥可说的。。。

(为了避免伤害眼睛已经删掉了调试语句

 #include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
#define vint vector<int>
#define vit vector<int>::iterator
using namespace std;
const int N=;
int p[N],noww[N],goal[N];
int rec[N],cst[N],inc[N],col[N];
int deg[N],aset[N],stk[N],vis[N];
int cal[N],cut[N],brk[N],spe[N],mae[N];
int n,dfn,cnt,tot,top; long long ans; vint ve[N],cr[N];
void Link(int f,int t)
{
noww[++cnt]=p[f];
goal[cnt]=t,p[f]=cnt;
}
int Finda(int x)
{
return x==aset[x]?x:aset[x]=Finda(aset[x]);
}
bool Bigcir()
{
int tmp=;
for(int i=;i<=n;i++)
{
if(Finda(i)==i) tmp++;
if(deg[i]!=) return false;
}
return tmp==;
}
void DFS(int nde)
{
stk[++top]=nde,vis[nde]=dfn;
for(int i=p[nde],g;i;i=noww[i])
if(!vis[g=goal[i]]) DFS(g);
else if(vis[g]==dfn)
{
int ori=g,pts=top; tot++;
while(stk[pts]!=ori)
{
int tmp=stk[pts--];
cr[tot].push_back(tmp);
col[tmp]=tot,inc[tmp]=true;
}
cr[tot].push_back(ori);
col[ori]=tot,inc[ori]=true;
}
top--;
}
int main()
{
scanf("%d",&n);
for(int i=;i<=n;i++) aset[i]=i;
for(int i=;i<=n;i++)
{
scanf("%d%d",&rec[i],&cst[i]);
Link(i,rec[i]),ve[rec[i]].push_back(i);
aset[Finda(i)]=Finda(rec[i]),deg[rec[i]]++;
}
if(Bigcir()) printf(""),exit();
else
{
cst[n+]=1e9;
for(int i=;i<=n;i++)
if(!vis[i]) dfn++,DFS(i);
for(int i=;i<=n;i++)
if(deg[i]>&&inc[i])
{
int t,o=,c=col[i],kao;
for(vit it=ve[i].begin();it!=ve[i].end();it++)
{
if(!cut[t=*it]&&cst[t]>cst[o]) o=t;
if(inc[t]) kao=t;
}
for(vit it=ve[i].begin();it!=ve[i].end();it++)
if(!cut[t=*it]&&t!=o)
{
cut[t]=true,mae[i]=max(mae[i],cst[t]);
if(t==kao) brk[c]=true;
}
cal[c]=true,spe[i]=true;
}
for(int i=;i<=n;i++)
if(deg[i]>&&!inc[i])
{
int t,o=;
for(vit it=ve[i].begin();it!=ve[i].end();it++)
if(!cut[t=*it]&&cst[t]>cst[o]) o=t;
for(vit it=ve[i].begin();it!=ve[i].end();it++)
if((t=*it)!=o) cut[t]=true,deg[rec[t]]--;
}
for(int i=;i<=tot;i++)
if(!cal[i])
{
int t,o=n+;
for(vit it=cr[i].begin();it!=cr[i].end();it++)
if(!cut[(t=*it)]&&cst[t]<cst[o]) o=t;
cut[o]=true,deg[rec[o]]--;
}
}
for(int i=;i<=n;i++)
if(cut[i]) ans+=cst[i];
for(int i=;i<=tot;i++)
if(cal[i]&&!brk[i])
{
int t; long long tep=1e18;
for(vit it=cr[i].begin();it!=cr[i].end();it++)
if(!spe[rec[t=*it]]) tep=min(tep,ans+cst[t]);
else tep=min(tep,ans+cst[t]-mae[rec[t]]);
ans=tep;
}
printf("%lld",ans);
return ;
}

T2

逐行地每次正反都做一遍DP,记录从哪里吃过来,对应地递归吃后面的

 #include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=,inf=0x3f3f3f3f;
int n,m,vis[N][N],dp[N][N];
char str[N][N];
void Mini(int &x,int y)
{
if(x>y) x=y;
}
bool Out(int a,int b)
{
return a<||b<||a>n||b>m;
}
int DFS(int x,int y,int t)
{
if(Out(x,y)) return ;
int &vs=vis[x][y];
if(~vs) return vs?:inf;
vs=; int ret=;
if(str[x][y]=='Z')
{
if(t<) ret+=DFS(x+,y,)+DFS(x,y+,);
else ret+=DFS(x-,y,)+DFS(x,y-,);
Mini(ret,inf);
}
else
{
if(t==||t==) ret+=DFS(x,y+,)+DFS(x-,y,);
else ret+=DFS(x+,y,)+DFS(x,y-,);
Mini(ret,inf);
}
if(ret!=inf) vs=;
return ret;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++)
scanf("%s",str[i]+);
memset(dp,0x3f,sizeof dp);
for(int i=;i<=n;i++)
{
memset(vis,-,sizeof vis);
for(int j=,t=;j<=m;j++)
t+=DFS(i,j,),Mini(t,inf),Mini(dp[i][j],t);
memset(vis,-,sizeof vis);
for(int j=m,t=;j;j--)
t+=DFS(i,j,),Mini(t,inf),Mini(dp[i][j],t);
}
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
{
if(dp[i][j]==inf) printf("-1");
else printf("%d",dp[i][j]);
j==m?puts(""):putchar(' ');
}
return ;
}

T3

观察到顺序不影响答案,分块打标记

注意分开处理两边零散块是有顺序的

(已经忘掉有分块这个东西了TAT

 #pragma GCC optimize(3)
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define vint vector<int>
#define gint greater<int>
#define prq priority_queue
using namespace std;
const int N=,Sq=;
int n,m,rd,lp,rp,tg,sqr,tot;
int sus[N],bel[N],lpt[Sq],rpt[Sq];
prq<int> mxx[Sq]; prq<int,vint,gint> tag[Sq];
void Pre()
{
sqr=sqrt(n)+,lpt[tot=]=;
for(int i=;i<=n;i++)
{
bel[i]=(i-)/sqr+;
if(i%sqr==) rpt[tot++]=i,lpt[tot]=i+;
}
n%sqr?rpt[tot]=n:tot--;
}
void Create(int b)
{
mxx[b]=prq<int> ();
for(int i=lpt[b];i<=rpt[b];i++) mxx[b].push(sus[i]);
}
void Release(int b)
{
if(!tag[b].empty())
{
for(int i=lpt[b];i<=rpt[b];i++)
if(tag[b].top()<sus[i])
tag[b].push(sus[i]),sus[i]=tag[b].top(),tag[b].pop();
}
tag[b]=prq<int,vint,gint> ();
}
int Round(int l,int r,int s)
{
if(bel[l]==bel[r])
{
int b=bel[l];
Release(b);
for(int i=l;i<=r;i++)
if(s<sus[i]) swap(s,sus[i]);
Create(b);
}
else
{
int b1=bel[l],b2=bel[r];
Release(b1);
for(int i=l;i<=rpt[b1];i++)
if(s<sus[i]) swap(s,sus[i]); Create(b1);
for(int i=b1+;i<=b2-;i++)
if(s<mxx[i].top())
{
int tmp=mxx[i].top(); mxx[i].pop();
mxx[i].push(s),tag[i].push(s),s=tmp;
}
Release(b2);
for(int i=lpt[b2];i<=r;i++)
if(s<sus[i]) swap(s,sus[i]); Create(b2);
}
return s;
}
int main()
{
scanf("%d%d",&n,&m),Pre();
for(int i=;i<=n;i++) scanf("%d",&sus[i]);
for(int i=;i<=tot;i++) Create(i);
for(int i=;i<=m;i++)
{
scanf("%d%d%d",&lp,&rp,&tg);
if(lp>rp) tg=Round(lp,n,tg),lp=;
printf("%d\n",Round(lp,rp,tg));
}
return ;
}

2019.3.19

肥肠爆芡,因为沙茶博主昨天在学校的煞笔食堂吃坏了肚子,所以这场考试咕咕了

开始补档

T1

一开始找x坐标或y坐标最大的点,然后看下一次转向,左转就走极角最大的,右转就走极角最小的

其实扫一遍就可以,然而我傻乎乎地每次都排了个序

 #include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=;
struct a{int xp,yp,idx;}o,pt[N],cp[N];
bool cmp(a x,a y)
{
return x.xp==y.xp?x.yp<y.yp:x.xp<y.xp;
}
long long Cha(a x,a y)
{
return 1ll*(x.xp-o.xp)*(y.yp-o.yp)-1ll*(y.xp-o.xp)*(x.yp-o.yp);
}
bool cop(a x,a y)
{
return Cha(x,y)<;
}
int n,m,vis[N],ans[N]; char str[N];
void Solve(int p,int l,int r,a q)
{
vis[q.idx]=true,ans[p]=q.idx;
if(p==n) return;
o=q,sort(cp+l,cp++r,cop);
if(str[p]=='L') Solve(p+,l,r-,cp[r]);
else Solve(p+,l+,r,cp[l]);
}
int main()
{
scanf("%d",&n);
for(int i=;i<=n;i++)
scanf("%d%d",&pt[i].xp,&pt[i].yp),pt[i].idx=i;
scanf("%s",str+);
sort(pt+,pt++n,cmp);
for(int i=;i<=n;i++) cp[i]=pt[i];
Solve(,,n-,pt[n]);
for(int i=;i<=n;i++) printf("%d ",ans[i]);
return ;
}

T2

推性质题,我们发现每个人感染的是一段区间的人,然后就转成了线段覆盖的方案数,树状数组优化

 #include<cstdio>
#include<cstring>
#include<algorithm>
#define vint vector<int>
#define vit vector<int>::iterator
using namespace std;
const int N=,mod=1e9+;
int n,ans,uni[N],spd[N],bit[N],pre[N],suf[N];
struct a{int pos,spe;}pt[N]; vint ve[N];
bool cmp(a x,a y){return x.pos<y.pos;}
void Add(int &x,int y){x+=y; if(x>=mod) x-=mod;}
void Modify(int x,int y)
{
while(x<=n)
Add(bit[x],y),x+=x&-x;
return;
}
int Query(int x)
{
int ret=;
while(x>)
Add(ret,bit[x]),x-=x&-x;
return ret;
}
int main()
{
register int i;
scanf("%d",&n);
for(i=;i<=n;i++)
scanf("%d%d",&pt[i].pos,&pt[i].spe),uni[i]=pt[i].spe;
sort(pt+,pt++n,cmp),sort(uni+,uni++n);
int lth=unique(uni+,uni++n)-uni-;
for(i=;i<=n;i++) spd[i]=lower_bound(uni+,uni++lth,pt[i].spe)-uni;
for(i=;i<=n;i++) pre[i]=max(pre[i-],spd[i]);
for(i=n,suf[n+]=1e9;i;i--) suf[i]=min(suf[i+],spd[i]);
for(int i=;i<=n;i++) ve[pre[i]].push_back(suf[i]);
for(int i=,t,tmp;i<=n;i++)
for(vit it=ve[i].begin();it!=ve[i].end();it++)
{
t=*it,tmp=(Query(i)-Query(t-)+(t<)+mod)%mod;
Modify(i,tmp); if(i==n) Add(ans,tmp);
}
printf("%d",ans);
return ;
}

T3

这题......

2019.3.18考试&2019.3.19考试&2019.3.21考试

(orz ztb tql)

2019.3.21

T1

容斥/点分治

不想写了摸鱼了咕咕咕了

2019.3.18考试&2019.3.19考试&2019.3.21考试

T2

转化成完全二分图的哈密顿回路,式子随便推推就有了

然后考场推到这里不会去重,搞出来一个整数划分,wsl

去重的方法是我们钦定第一个回路经过左边第一个,然后就有

2019.3.18考试&2019.3.19考试&2019.3.21考试

T3

orz yzh tql

模拟费用流,在每一只鸟进来之后找离这只鸟最近的还有空的点,正确性参考费用流

具体来说要支持树上边权取反和查询到所有合法点的最短路,因为树高只有log暴力爬树DP维护

 #include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=,inf=1e9;
int nes[N],gu[N],flw[N],dp[N],bes[N],n,m,ans;
void Maintain(int nde)
{
int ls=*nde,rs=*nde+; dp[nde]=inf;
if(ls<=n&&dp[ls]+(flw[ls]>=?:-)<dp[nde])
dp[nde]=dp[ls]+(flw[ls]>=?:-),bes[nde]=bes[ls];
if(rs<=n&&dp[rs]+(flw[rs]>=?:-)<dp[nde])
dp[nde]=dp[rs]+(flw[rs]>=?:-),bes[nde]=bes[rs];
if(nes[nde]&&dp[nde]>=) dp[nde]=,bes[nde]=nde;
}
void Pushup(int nde)
{
Maintain(nde);
if(nde!=) Pushup(nde>>);
}
void Gu(int nde)
{
int x=nde,tmp=,bsf=inf,bsp=,bsn=;
while(x)
{
int tep=tmp+dp[x];
if(tep<bsf) bsf=tep,bsp=bes[x],bsn=x;
tmp+=(flw[x]<=?:-),x>>=;
}
ans+=bsf,nes[bsp]--;
int mem1=nde,mem2=bsp;
while(nde!=bsn) flw[nde]--,nde>>=;
while(bsp!=bsn) flw[bsp]++,bsp>>=;
Pushup(mem1),Pushup(mem2);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++) scanf("%d",&nes[i]);
for(int i=;i<=m;i++) scanf("%d",&gu[i]);
for(int i=n;i;i--) Maintain(i);
for(int i=;i<=m;i++) Gu(gu[i]),printf("%d ",ans);
return ;
}