模拟17 考试总结
看上去是暴力场,不过依旧炸裂
考试经过
基本操作,看T1,第一眼tarjan,然后自我否定,发现很暴力的样子,于是直接开搜,先套了2个循环,发现跑不出大样例,调半天还是跑不出来,自闭。。。去开T2,
看着像NP完全,果断dfs,,,由于XIN team学的不好觉得可能会假,于是特判上,回来想到T1可以bfs,于是成功过掉大样例收下40。T3没多少时间,上来就测试点分治,乱搞半天,最后40+10+0=50。依旧是少得可怜的分数。。。
T1.世界线
对于每个点,所有他能到的点都要算,再减去已经连的边就是贡献,正解也是这样,用记忆化优化一下,再用bitset砍常数就行了
内存可能会炸,开个池子回收就行了
#include <bits/stdc++.h>
using namespace std;
const int N=100050;
struct node{
int from,to,next;
}a[2*N];
int head[N],mm=1;
inline void add(int x,int y)
{
a[mm].from=x;a[mm].to=y;
a[mm].next=head[x];head[x]=mm++;
}
int b[N],c[N];
int an;queue <int> q;
bitset <60050> v[30050];
int pl[N],p,mp[N],num;
inline void kill(int x)
{
v[mp[x]].reset();
pl[++p]=mp[x];
}
inline void newid(int x)
{
if(p)mp[x]=pl[p--];
else mp[x]=++num;
}
long long ans;int n,m;
inline void doit()
{
for(int i=1;i<=n;i++)if(!c[i])q.push(i);
while(!q.empty())
{
int x=q.front();if(!mp[x])newid(x);
ans+=v[mp[x]].count()-b[x];
for(int i=head[x];i;i=a[i].next)
{
int y=a[i].to;if(!mp[y])newid(y);
v[mp[y]]|=v[mp[x]];v[mp[y]][x]=1;
c[y]--;if(!c[y])q.push(y);
}
kill(x);q.pop();
}
}
signed main()
{
cin>>n>>m;
for(int i=1;i<=m;i++)
{
int x,y;scanf("%d%d",&x,&y);
add(y,x);b[x]++;c[x]++;
}
doit();cout<<ans;
return 0;
}
数据不卡,所以能过,对于极限数据chy使用了一套组合拳完美解决
大意是,卡空间(菊花)就用时间换,卡时间(链)就记搜,遇见蒲公英就一半一半,正好,%%%
T2.时间机器
就是个贪心。。。
把电阻和零件从小到大排序,每次选左端点在他左边的电阻中,右端点最靠左的,正确性显然
用set查后继,用完了就删除,JYF赛事A掉,忒强了
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define py puts("Yes")
#define pn puts("No")
inline int read()
{
int x=0;char ch=getchar();
while(ch<'0'||ch>'9'){ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x;
}
const int N=50050;
struct node{
int l,r,s,op,id;
friend bool operator <(node a1,node b1)
{
if(a1.r!=b1.r)return a1.r<b1.r;
else return a1.id>b1.id;
}
}a[2*N];
set <node> s;
inline bool cmp(node x,node y)
{
if(x.l!=y.l)return x.l<y.l;
else return x.op<y.op;
}
signed main()
{
int t;cin>>t;
while(t--)
{
int n,m;cin>>n>>m;s.clear();
for(int i=1;i<=n;i++)a[i].l=read(),a[i].r=read(),a[i].s=read(),a[i].op=1;
for(int i=n+1;i<=n+m;i++)a[i].l=read(),a[i].r=read(),a[i].s=read(),a[i].op=0;
sort(a+1,a+1+n+m,cmp);for(int i=1;i<=n+m;i++)a[i].id=i;
node sb;sb.r=1e9;sb.id=1e9;s.insert(sb);
bool flag=0;
for(int i=1;i<=n+m;i++)
{
if(a[i].op==0)s.insert(a[i]);
else
while(a[i].s)
{
node p=*s.lower_bound(a[i]);
if(p.r==1e9){flag=1;break;}
int pp=p.id;int k=min(a[i].s,a[pp].s);
a[i].s-=k;a[pp].s-=k;
if(!a[pp].s)s.erase(p);
}
if(flag)break;
}
if(flag)pn;
else py;
}
}
T3.weight
反正我看不出这是数据结构板子。。。
先求最小生成树,然后分情况讨论:
对于非树边,他的权值最大是他两个端点对应树上最大权值-1,不然一定存在一种方案不选他
对于树边,他会被非树边影响,只要存在一条非树边,那么这两个端点间的所有树边都要比那个非树边的权值减一还小
所以分别对应区间求max,区间取min,树刨就行
用两个线段树分别维护两个操作,不然贼乱虽然这样也很乱
不得不说300行真刺激啊
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=100050;
struct node{
int from,to,next,w,id,ans;bool ok=0;
friend bool operator <(node x,node y)
{
return x.w>y.w;
}
}a[2*N];
priority_queue <node> q;
int head1[N],mm1=1;
inline void add(int x,int y,int z)
{
a[mm1].from=x;a[mm1].to=y;a[mm1].w=z;a[mm1].id=mm1;
a[mm1].next=head1[x];q.push(a[mm1]);head1[x]=mm1++;
}
struct newnode{
int from,to,next,w;
}b[2*N];
int head2[N],mm2=1;
inline void addd(int x,int y,int z)
{
b[mm2].from=x;b[mm2].to=y;b[mm2].w=z;
b[mm2].next=head2[x];head2[x]=mm2++;
}
int f[N];int n,m,sb;
inline int find(int x)
{
if(f[x]!=x)f[x]=find(f[x]);
return f[x];
}
inline void klskr()
{
for(int i=1;i<=n;i++)f[i]=i;
while(!q.empty())
{
node nd=q.top();
int x=nd.from,y=nd.to,w=nd.w;
if(find(x)!=find(y))
{
addd(x,y,w);addd(y,x,w);
a[nd.id].ok=1;
f[find(x)]=find(y);
}
q.pop();
}
}
int vis[N],fa[N],son[N],size[N],d[N];
int id[N],rk[N],top[N],cnt,v[N];
inline void dfs1(int x)
{
vis[x]=1;
for(int i=head2[x];i;i=b[i].next)
{
int y=b[i].to;
if(vis[y])continue;
d[y]=d[x]+1;fa[y]=x;
dfs1(y);
size[x]+=size[y];
if(size[y]>size[son[x]])
son[x]=y;
}
size[x]++;
}
inline void dfs2(int x,int h)
{
if(!x)return ;
vis[x]=1;top[x]=h;
id[x]=++cnt;rk[cnt]=x;
dfs2(son[x],h);
for(int i=head2[x];i;i=b[i].next)
{
int y=b[i].to;
if(vis[y])continue;
if(y==son[x])continue;
dfs2(y,y);
}
}
inline void dfs(int x)
{
vis[x]=1;
for(int i=head2[x];i;i=b[i].next)
{
int y=b[i].to;
if(vis[y])continue;
v[y]=b[i].w;dfs(y);
}
}
inline int getlca(int x,int y)
{
int fx=top[x],fy=top[y];
while(fx!=fy)
{
if(d[fx]<d[fy])swap(x,y),swap(fx,fy);
x=fa[fx];fx=top[x];
}
if(id[x]>id[y])swap(x,y);
return x;
}
struct tree1{
int l,r,ma;
}tr1[4*N];
inline void build1(int id,int l,int r)
{
tr1[id].l=l;tr1[id].r=r;
if(l==r)
{
tr1[id].ma=v[rk[l]];
return;
}
int mid=(l+r)>>1;
build1(id*2,l,mid);build1(id*2+1,mid+1,r);
tr1[id].ma=max(tr1[id*2].ma,tr1[id*2+1].ma);
}
inline void change1(int id,int p,int v)
{
if(tr1[id].l==tr1[id].r)
{
tr1[id].ma=v;
return;
}
int mid=(tr1[id].l+tr1[id].r)>>1;
if(p<=mid)change1(id*2,p,v);
else change1(id*2+1,p,v);
tr1[id].ma=max(tr1[id*2].ma,tr1[id*2+1].ma);
}
inline int getmax(int id,int l,int r)
{
if(l<=tr1[id].l&&r>=tr1[id].r)return tr1[id].ma;
int mid=(tr1[id].l+tr1[id].r)>>1;
if(r<=mid)return getmax(id*2,l,r);
if(l>mid)return getmax(id*2+1,l,r);
return max(getmax(id*2,l,mid),getmax(id*2+1,mid+1,r));
}
struct tree2{
int l,r,mi,lazy=-1;
}tr2[4*N];
inline void build2(int id,int l,int r)
{
tr2[id].l=l;tr2[id].r=r;
if(l==r)
{
tr2[id].mi=1e12;
tr2[id].lazy=1e12;
return;
}
int mid=(l+r)>>1;
build2(id*2,l,mid);build2(id*2+1,mid+1,r);
tr2[id].lazy=1e12;
tr2[id].mi=min(tr2[id*2].mi,tr2[id*2+1].mi);
}
inline void luo(int id)
{
if(tr2[id].lazy!=1e12&&tr2[id].l!=tr2[id].r)
{
tr2[id*2].lazy=min(tr2[id*2].lazy,tr2[id].lazy);
tr2[id*2+1].lazy=min(tr2[id*2+1].lazy,tr2[id].lazy);
tr2[id*2].mi=min(tr2[id*2].mi,tr2[id].lazy);
tr2[id*2+1].mi=min(tr2[id*2+1].mi,tr2[id].lazy);
tr2[id].lazy=1e12;
}
}
inline void change2(int id,int p,int v)
{
if(tr2[id].l==tr2[id].r)
{
tr2[id].mi=v;
return;
}
luo(id);
int mid=(tr2[id].l+tr2[id].r)>>1;
if(p<=mid)change2(id*2,p,v);
else change2(id*2+1,p,v);
tr2[id].mi=min(tr2[id*2].mi,tr2[id*2+1].mi);
}
inline int getmin(int id,int l,int r)
{
if(l<=tr2[id].l&&r>=tr2[id].r)return tr2[id].mi;
luo(id);int mid=(tr2[id].l+tr2[id].r)>>1;
if(r<=mid)return getmin(id*2,l,r);
if(l>mid)return getmin(id*2+1,l,r);
return min(getmin(id*2,l,mid),getmin(id*2+1,mid+1,r));
}
inline void domin(int id,int l,int r,int v)
{
if(l<=tr2[id].l&&r>=tr2[id].r)
{
tr2[id].lazy=min(tr2[id].lazy,v);
tr2[id].mi=min(tr2[id].mi,v);
return;
}
luo(id);int mid=(tr2[id].l+tr2[id].r)>>1;
if(r<=mid)domin(id*2,l,r,v);
else if(l>mid)domin(id*2+1,l,r,v);
else domin(id*2,l,r,v),domin(id*2+1,l,r,v);
tr2[id].mi=min(tr2[id*2].mi,tr2[id*2+1].mi);
}
inline int ganmax(int x,int y)
{
int ans=0,fx=top[x],fy=top[y];
while(fx!=fy)
{
if(d[fx]<d[fy])swap(x,y),swap(fx,fy);
ans=max(ans,getmax(1,id[fx],id[x]));
x=fa[fx];fx=top[x];
}
if(id[x]>id[y])swap(x,y);
ans=max(ans,getmax(1,id[x],id[y]));
return ans;
}
inline void gan(int x,int y,int v)
{
int fx=top[x],fy=top[y];
while(fx!=fy)
{
if(d[fx]<d[fy])swap(x,y),swap(fx,fy);
domin(1,id[fx],id[x],v);
x=fa[fx];fx=top[x];
}
if(id[x]>id[y])swap(x,y);
domin(1,id[x],id[y],v);
}
signed main()
{
cin>>n>>m>>sb;
for(int i=1;i<=m;i++)
{
int x,y,z;scanf("%lld%lld%lld",&x,&y,&z);
add(x,y,z);
}
klskr();d[1]=1;dfs1(1);
memset(vis,0,sizeof(vis));dfs2(1,1);
memset(vis,0,sizeof(vis));dfs(1);
build1(1,1,cnt);build2(1,1,cnt);
for(int i=1;i<mm1;i++)
{
if(!a[i].ok)
{
int x=a[i].from,y=a[i].to;
int lca=getlca(x,y),p=getmax(1,id[lca],id[lca]);
change1(1,id[lca],-1e9);
a[i].ans=ganmax(x,y)-1;
change1(1,id[lca],p);
int pp=getmin(1,id[lca],id[lca]);
gan(x,y,a[i].w-1);change2(1,id[lca],pp);
}
}
for(int i=1;i<mm1;i++)
{
if(a[i].ok)
{
int x=a[i].from,y=a[i].to;
if(d[x]<d[y])swap(x,y);
a[i].ans=getmin(1,id[x],id[x]);
if(a[i].ans==1e12)a[i].ans=-1;
}
}
for(int i=1;i<mm1;i++)printf("%lld ",a[i].ans);
return 0;
}
怪吓人的
对于区间取min,由于你只维护min值,所以直接打标记就行了
考试总结
1.对于T1这种,要有意识的优化,很可能暴力差一步就是正解
2.看见T2这种就要有贪心的敏感,要相信题是可做的,别自我放弃
3.打部分分要么打稳,要么就别打,沉迷测试点分治一旦失败就跟爆零没啥区别了

浙公网安备 33010602011771号