今天连续3道题都出锅...无F♂A可说
T1 题意简述:jzoj5775
Description
农夫约也有自己的假期呀!他要去海边度假,然而mm和hh不能离开他。没办法,他只好把他们两个带上。
到了海边,农夫约把他的羊放在一个(n*n)的矩阵(有n*n个方格)里。mm和hh十分好动,他们要走到m(m<=n*n)个地方,第i个地方的坐标为(x[i](行),y[i](列)),每到一个地方他们会高歌一曲,制造q[i]点魔音值,因为他们的魔音十分独特,他们的声音只能横着或竖着传播。每传播一格,魔音值会增加1。(传播的格子数取最小的)接下来农夫约要住酒店。为了方便照顾小羊们,他选的酒店的坐标要在矩阵内。但小羊们的魔音让他十分头疼。他想求出魔音值最小的地方。
他还要享受他的假期,所以他把这个任务交给你了,加油(^_^)。
Input
接下来m行,每行3个正整数x[i],y[i]和q[i]。
Output
接下来一行两个正整数zb1和zb2,表示魔音值最小的地方的坐标(如果有多个答案,输出横坐标最小的情况下,纵坐标最小的)。
Data Constraint
30%的数据,n<=1000.
100%的数据,0<n<=100000,0<m<=100000,0<z<=10,0<q[i]<=100.
解题思路:还算简单...
考虑先暴力算出(1,1)的魔音值,然后递推。
(x,y)->(x+1,y) 魔音值增加了1~x行中起始点的数量,减少了x+1~n行中起始点的数量。
(x,y)->(x-1,y) 魔音值增加了x~n行中起始点的数量,减少了1~x-1行中起始点的数量。
(x,y)->(x,y+1) 魔音值增加了1~y列中起始点的数量,减少了y+1~n列中起始点的数量。
(x,y)->(x,y-1) 魔音值增加了y~n列中起始点的数量,减少了1~y-1列中起始点的数量。
发现可以求每行和每列的前缀和,可以把时间复杂度压到O(2n)。
有细心的观众可能会说:z呢?
emmm...由于出题人乱出数据,导致所有数据点的z都默认为1。
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> #define ll long long using namespace std; ll n,m,k,sumx[100001],sumy[100001],sum; int main() { freopen("shuru.in","r",stdin); freopen("shuru.out","w",stdout); scanf("%lld%lld%lld",&n,&m,&k); for(ll i=1;i<=m;i++) { ll u,v,w; scanf("%lld%lld%lld",&u,&v,&w); sumx[u]++; sumy[v]++; sum+=w; } for(ll i=1;i<=n;i++) sum+=sumx[i]*(i-1)+sumy[i]*(i-1); for(ll i=2;i<=n;i++) sumx[i]+=sumx[i-1],sumy[i]+=sumy[i-1]; ll xsum=sum,ysum=sum,mnx=sum,ansx=1,mny=sum,ansy=1; for(ll i=2;i<=n;i++) { xsum+=2*sumx[i-1]-sumx[n]; if(xsum<mnx) mnx=xsum,ansx=i; ysum+=2*sumy[i-1]-sumy[n]; if(ysum<mny) mny=ysum,ansy=i; } printf("%lld\n%lld %lld\n",mnx+mny-sum,ansx,ansy); return 0; }
T2 题意简述:jzoj5776
Description
Input
第二行n个数ai,表示每个平台上的加速器的性能。
第三至n+1行,每行三个数bi,ci,di分别表示这条无向边的起点,终点与耗费的能量值
Output
第二行一个数,表示最小耗费的体力值。
Data Constraint
对于40%的数据:n<=1000
对于60%的数据:n<=8000
对于80%的数据:n<=100000
对于100%的数据:0<n<=700000;ai<=1000;1<=bi,ci<=n;di<=1000。
数据保证一个点的加速器性能绝对小于等于它的所有的边所耗费的能量,保证所有节点都可以到达,保证没有数据与样例相同。
解题思路:和第一题基本相同。
先dfs求出一个点的结果,同时把每个点的子树大小算出。
然后dfs,每次查询到某个点时修改它与父亲的子树大小及边权,然后算出这个点的结果。
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> #include<queue> #define INF 0x3f3f3f3f #define ll long long using namespace std; ll n,mn=INF,pnt,cnt,head[700001],rdc[700001],siz[700001],ans[700001]; struct uio{ ll nxt,to,val; }edge[1400001]; void add(ll x,ll y,ll z) { edge[++cnt].nxt=head[x]; edge[cnt].to=y; edge[cnt].val=z; head[x]=cnt; } ll dfs(ll x,ll fa) { siz[x]=1; ll tmp=0; for(ll i=head[x];i;i=edge[i].nxt) { ll y=edge[i].to; if(y==fa) continue; tmp+=dfs(y,x)+edge[i].val*siz[y]; siz[x]+=siz[y]; } return tmp; } void dt(ll x,ll fa) { for(ll i=head[x];i;i=edge[i].nxt) { ll y=edge[i].to; if(y==fa) continue; ans[y]=ans[x]-edge[i].val*siz[y]; siz[x]-=siz[y],siz[y]+=siz[x]; edge[i].val+=rdc[x]-rdc[y]; ans[y]+=edge[i].val*siz[x]; if(ans[y]<mn) mn=ans[y],pnt=y; if(ans[y]==mn&&y<pnt) pnt=y; dt(y,x); siz[y]-=siz[x],siz[x]+=siz[y]; edge[i].val+=rdc[y]-rdc[x]; } } int main() { freopen("yggdrasil.in","r",stdin); freopen("yggdrasil.out","w",stdout); scanf("%lld",&n); for(ll i=1;i<=n;i++) scanf("%lld",&rdc[i]); for(ll i=1;i<n;i++) { ll u,v,w; scanf("%lld%lld%lld",&u,&v,&w); add(u,v,w-rdc[u]); add(v,u,w-rdc[v]); } ans[1]=dfs(1,0),mn=ans[1],pnt=1; dt(1,0); printf("%lld\n%lld\n",pnt,mn); return 0; }
T3 题意简述:jzoj5786
Description
因为经过10^9^9^9年后,
他得到了一个新技能——观察大法。
刚出来的infleaking就想要挑战自我。
为什么infleaking会这么自信呢?
因为infleaking做到了可以通过观察数据就就可以得出答案。
但是出题人十分不服,想要将infleaking的气焰打压下去,
于是想到了一道题。
结果被infleaking运用他那强大的观察能力看完数据后给出了答案。
怎么能够让infleaking继续下去呢,出题人于是就将数据重出并且加密了。
没有能直接观察数据的infleaking十分不服气,想要解决这道题,
但是苦于不能直接使用他的新技能,所以想要请聪明的你帮infleaking解决这个问题。
出题人给出一颗以1为根的树,一开始每个节点都是一颗棋子,一面白一面黑,白色的面朝上
接下来就q次操作,操作分两种
0操作 将一个颗棋子翻转
1操作 询问一颗棋子与所有面朝上为黑色的棋子lca最深的那个的编号
Input
第2行,一共n-1个正整数,第i个正整数表示i+1号结点的父亲
第3~q+3每行两个整数x ,第|x|个为被操作的棋子,x>0操作为0否则为1
Output
Data Constraint
![](https://img-blog.csdnimg.cn/2022010617242419268.png)
解题思路:很...麻...烦...
倍增LCA+线段树维护 <-- 这是出题人的思路。
由Menteur_Hxy大佬亲身试验,倍增是会T的。
由ErkkiErkko大佬亲身试验,set也是会T的。
由本蒟蒻亲身试验,就算用树剖+线段树,也是会RE的。
(顺带一提,我RE的那两个点以上两位大佬也RE了)
......出题人TQL!!!
以下是90分RE代码。
8.10 fix:
经过Menteur_Hxy大佬试验,发现RE原因系DFS爆栈。
解决方法:用BFS求DFS序/手写栈。
鉴于博主太懒,这里贴出Menteur_Hxy大佬的博客地址:
https://www.cnblogs.com/Menteur-Hxy/p/9456872.html
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> #include<set> #define INF 0x3f3f3f3f using namespace std; int n,m,cnt,tot,head[800001]; int hvyson[800001],fa[800001],siz[800001],dep[800001],top[800001],dfsno[800001],vis[800001],mp[800001]; int num[3200001]; struct uio{ int nxt,to; }edge[1600001]; //set<int> st; //set<int>::iterator it; void add(int x,int y) { edge[++cnt].nxt=head[x]; edge[cnt].to=y; head[x]=cnt; } void dfs1(int x,int f,int depth) { dep[x]=depth; fa[x]=f; siz[x]=1; int maxson=-1; for(int i=head[x];i;i=edge[i].nxt) { int y=edge[i].to; if(y==f) continue; dfs1(y,x,depth+1); siz[x]+=siz[y]; if(siz[y]>maxson) { hvyson[x]=y; maxson=siz[y]; } } } void dfs2(int x,int topf) { dfsno[x]=++tot; mp[tot]=x; top[x]=topf; if(!hvyson[x]) return; dfs2(hvyson[x],topf); for(int i=head[x];i;i=edge[i].nxt) { int y=edge[i].to; if(y==fa[x]||y==hvyson[x]) continue; dfs2(y,y); } } int lca(int x,int y) { while(top[x]!=top[y]) { if(dep[top[x]]<dep[top[y]]) swap(x,y); x=fa[top[x]]; } return dep[x]<dep[y]? x:y; } void revise(int now,int l,int r,int x,int y) { if(l==r) { num[now]+=y; return; } int mid=(l+r)>>1; if(x<=mid) revise(now<<1,l,mid,x,y); else revise(now<<1|1,mid+1,r,x,y); num[now]=num[now<<1]+num[now<<1|1]; } int query(int now,int l,int r,int x) { if(l==r&&num[now]) return 1; else if(l==r&&!num[now]) return 0; int mid=(l+r)>>1; if(x<=mid) return query(now<<1,l,mid,x); else return query(now<<1|1,mid+1,r,x); } int query1(int now,int l,int r,int L,int R) { if(!num[now]) return INF; if(l==r) return l; if(L<=l&&r<=R) { int mid=(l+r)>>1; if(num[now<<1|1]) return query1(now<<1|1,mid+1,r,L,R); else return query1(now<<1,l,mid,L,R); } int mid=(l+r)>>1; if(mid>=R) return query1(now<<1,l,mid,L,R); else { int tmp=INF; if(num[now<<1|1]) tmp=query1(now<<1|1,mid+1,r,L,R); if(tmp!=INF) return tmp; else return query1(now<<1,l,mid,L,R); } } int query2(int now,int l,int r,int L,int R) { if(!num[now]) return INF; if(l==r) return l; if(L<=l&&r<=R) { int mid=(l+r)>>1; if(num[now<<1]) return query2(now<<1,l,mid,L,R); else return query2(now<<1|1,mid+1,r,L,R); } int mid=(l+r)>>1; if(mid+1<=L) return query2(now<<1|1,mid+1,r,L,R); else { int tmp=INF; if(num[now<<1]) tmp=query2(now<<1,l,mid,L,R); if(tmp!=INF) return tmp; else return query2(now<<1|1,mid+1,r,L,R); } } int main() { freopen("watch.in","r",stdin); freopen("watch.out","w",stdout); scanf("%d%d",&n,&m); for(int i=1;i<n;i++) { int u; scanf("%d",&u); add(i+1,u),add(u,i+1); } dfs1(1,0,1),dfs2(1,1); for(int i=1;i<=m;i++) { int u; scanf("%d",&u); if(u>0&&!vis[u]) {revise(1,1,n,dfsno[u],1);vis[u]=1;continue;} else if(u>0&&vis[u]) {revise(1,1,n,dfsno[u],-1);vis[u]=0;continue;} if(!num[1]) {printf("0\n");continue;} u=-u; int c=query(1,1,n,dfsno[u]); if(c) {printf("%d\n",u);continue;} int a=query1(1,1,n,1,dfsno[u]-1); int b=query2(1,1,n,dfsno[u]+1,n); if(a!=INF) a=lca(u,mp[a]); if(b!=INF) b=lca(u,mp[b]); if(b==INF) printf("%d\n",a); else if(a==INF) printf("%d\n",b); else printf("%d\n",(dep[a]>dep[b]? a:b)); // if(u>0&&!vis[u]) {st.insert(dfsno[u]);vis[u]=1;continue;} // else if(u>0&&vis[u]) {st.erase(dfsno[u]);vis[u]=0;continue;} // if(st.empty()) {printf("0\n");continue;} // u=-u; // if(st.count(u)) {printf("%d\n",u);continue;} // it=st.upper_bound(dfsno[u]); // if(it!=st.end()) // { // a=lca(u,mp[*it]); // if(it==st.begin()) // {printf("%d\n",a);continue;} // it--; // b=lca(u,mp[*it]); // printf("%d\n",(dep[a]>dep[b]? a:b)); // continue; // } // it--; // b=lca(u,mp[*it]); // printf("%d\n",b); } return 0; }