2018.8.9提高B组模拟考试

今天连续3道题都出锅...无F♂A可说

 

T1 题意简述:jzoj5775

 

Description

  在某国有一个叫农夫约的人,他养了很多羊,其中有两头名叫mm和hh,他们的歌声十分好听,被当地人称为“魔音”······
  农夫约也有自己的假期呀!他要去海边度假,然而mm和hh不能离开他。没办法,他只好把他们两个带上。
  到了海边,农夫约把他的羊放在一个(n*n)的矩阵(有n*n个方格)里。mm和hh十分好动,他们要走到m(m<=n*n)个地方,第i个地方的坐标为(x[i](行),y[i](列)),每到一个地方他们会高歌一曲,制造q[i]点魔音值,因为他们的魔音十分独特,他们的声音只能横着或竖着传播。每传播一格,魔音值会增加1。(传播的格子数取最小的)接下来农夫约要住酒店。为了方便照顾小羊们,他选的酒店的坐标要在矩阵内。但小羊们的魔音让他十分头疼。他想求出魔音值最小的地方。
  他还要享受他的假期,所以他把这个任务交给你了,加油(^_^)。

Input

第一行输入n、m和z。
接下来m行,每行3个正整数x[i],y[i]和q[i]。

Output

第一行一个整数表示魔音值最小是多少。
接下来一行两个正整数zb1和zb2,表示魔音值最小的地方的坐标(如果有多个答案,输出横坐标最小的情况下,纵坐标最小的)。

Data Constraint

10%的数据,n<=10(来自题目的馈赠).
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

小x得到了一个(不可靠的)小道消息,传说中的神岛阿瓦隆在格陵兰海的某处,据说那里埋藏着亚瑟王的宝藏,这引起了小x的好奇,但当他想前往阿瓦隆时发现那里只有圣诞节时才能到达,然而现在已经春天了,不甘心的他将自己的目的地改成了世界树,他耗费了大量的时间,终于将自己传送到了世界树下。世界树是一棵非常巨大的树,它有着许许多多的枝条以及节点,每个节点上都有一个平台。好不容易来到传说中的世界树下,小x当然要爬上去看看风景。小x每经过一条边都会耗费体力值。然而世界树之主想给他弄(gáo)些(dǐan)麻(shì)烦(qíng),于是他在每条边上都设了一个魔法阵,当小x踏上那条边时会被传送回根节点,魔法阵只生效一次。这岂不是要累死小x?幸运的是,每个平台上都有无数个加速器,这些加速器可以让小x在当前节点所连的边上耗费的体力值减少,不同平台的加速器性能不一定相同,但同一个平台的加速器性能绝对相同。世界树之主给了小x一次“换根”的机会,他可以将世界树的任何一个节点变为根,但所有的边都不能改变。小x想问你,将根换为哪个节点能使小x爬到世界树上的每个节点耗费的体力值和最少。默认编号为1的点为初始根。

Input

第一行一个数n,表示有n个节点。
第二行n个数ai,表示每个平台上的加速器的性能。
第三至n+1行,每行三个数bi,ci,di分别表示这条无向边的起点,终点与耗费的能量值

Output

第一行一个数,表示要换成的节点,如果有多个点为根时耗费的体力值都最小,则输出编号最小的那个。如果保持为1是最优的,就输出1。
第二行一个数,表示最小耗费的体力值。

Data Constraint

对于20%的数据:n<=100
对于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

infleaking十分愉快地走在路上,
因为经过10^9^9^9年后,
他得到了一个新技能——观察大法。
刚出来的infleaking就想要挑战自我。
为什么infleaking会这么自信呢?
因为infleaking做到了可以通过观察数据就就可以得出答案。
但是出题人十分不服,想要将infleaking的气焰打压下去,
于是想到了一道题。
结果被infleaking运用他那强大的观察能力看完数据后给出了答案。
怎么能够让infleaking继续下去呢,出题人于是就将数据重出并且加密了。
没有能直接观察数据的infleaking十分不服气,想要解决这道题,
但是苦于不能直接使用他的新技能,所以想要请聪明的你帮infleaking解决这个问题。
 
出题人给出一颗以1为根的树,一开始每个节点都是一颗棋子,一面白一面黑,白色的面朝上
接下来就q次操作,操作分两种
0操作 将一个颗棋子翻转
1操作 询问一颗棋子与所有面朝上为黑色的棋子lca最深的那个的编号

Input

第1行,两个正整数n,q
第2行,一共n-1个正整数,第i个正整数表示i+1号结点的父亲
第3~q+3每行两个整数x ,第|x|个为被操作的棋子,x>0操作为0否则为1

Output

对于每个op为1的操作输出对应的编号,若场上没有黑棋子输出0

Data Constraint

 

   解题思路:很...麻...烦...

             倍增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;
}

 

posted @ 2018-08-09 22:31  radishえらい  阅读(354)  评论(0编辑  收藏  举报