2018.8.9提高B组模拟考试

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

T1 题意简述:jzoj5775

解题思路:还算简单...

考虑先暴力算出(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

解题思路:和第一题基本相同。

先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

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

倍增LCA+线段树维护 <-- 这是出题人的思路。

Menteur_Hxy大佬亲身试验,倍增是会T的

ErkkiErkko大佬亲身试验,set也是会T的

由本蒟蒻亲身试验,就算用树剖+线段树,也是会RE的。

......

#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;
}

原文地址:https://www.cnblogs.com/water-radish/p/9452260.html

时间: 2024-11-02 10:33:44

2018.8.9提高B组模拟考试的相关文章

2018.8.6提高A组模拟考试

emmm看起来A组的题也没比B组难多少嘛... T1 题意简述:jzoj5796 解题思路:首先发现数字是输入数据中最特殊的字符,因此可以在找到数字后向四周dfs找到水箱边界. 其次发现题目中说“不会把水管分叉也不会出现水管交叉的情况”,因此可以在找到水箱后用dfs求 出每根水管的位置及其连接的水箱. 然后根据基本的物理原理(牛顿笑着躺下了)发现若同一水箱所连的两根水管所处位置高低不同,那 么在填满低水管所连的水箱之前,高水管所连的水箱是不会有水进入的. 综上,只需dfs一遍即可得出水箱灌满的次

2018.8.10提高B组模拟考试

为了迎合今天的讲课内容——数论,A组和B组都各出了两道数学. 对于完全不会数论的博主来说,这简直是灾难. T1 题意简述:jzoj5791 解题思路:看到这道题,首先想到对n个数分别分解成质数后存在数组里. 然后呢?枚举ans吗? 其实可以二分答案,加上一个求质数个数的技巧就能过. 发现cnt[2]=ans/2+ans/4+ans/8+... cnt[3]=ans/3+ans/9+ans/27+... 本题结束. #include<iostream> #include<cstdio>

2018.8.7提高B组模拟考试

T1 题意简述:jzoj5461 解题思路:贪心. 考虑先用完k张优惠券,只需将商品按优惠价排序,依次判断钱是否足够,若是则ans++,否则直接退 出循环并输出ans即可. k张优惠券用完后若钱还未用完,则考虑用前面商品的优惠券买后面的商品.注意要把未买的商品按 原价重新排序.只需把用了优惠券的商品的原价与优惠价之差价放进一个小根堆,每次判断:此商品 原价是否小于堆顶商品差价+此商品优惠价.若是则直接用原价买即可,否则用堆顶商品差价+此商品 优惠价买即可.注意有0张优惠券的情况,因此要特判队列是

【NOIP2016提高A组模拟8.15】Password

题目 分析 首先我们知道,原A序列其实表示一个矩阵,而这个矩阵的对角线上的数字就是答案B序列. 接着\(a.b>=gcd(a,b)\),所以序列A中的最大的数就是ans[1],第二大的数就是ans[2]. 但是ans[3]并不一定就是序列A中的第三大的数,因为gcd(ans[1],ans[2])有可能是序列A中的第三大的数. 所以但找到了ans[i],对于每个gcd(ans[i],ans[1~i-1])在序列A中删掉两个(就是删掉2(i-1)个.为什么是两个自己考虑).时间复杂度\(O(n^2l

【NOIP2016提高A组模拟8.14】传送带

题目 在一个2维平面上有两条传送带,每一条传送带可以看成是一条线段.两条传送带分别为线段AB和线段CD.FTD在AB上的移动速度为P,在CD上的移动速度为Q,在平面上的移动速度R.现在FTD想从A点走到D点,他想知道最少需要走多长时间 分析 易得,答案就是首先在AB上走一段,然后走到CD上的一点,再走到D. 正解就是三分套三分,但本人很懒,打了个枚举加三分,勉强卡了过去. 首先在AB上枚举一点,接着在CD上按时间三分. #include <cmath> #include <iostrea

【NOIP2016提高A组模拟8.14】疯狂的火神

题目 火神为了检验zone的力量,他决定单挑n个人. 由于火神训练时间有限,最多只有t分钟,所以他可以选择一部分人来单挑,由于有丽子的帮助,他得到了每个人特定的价值,每个人的价值由一个三元组(a,b,c)组成,表示如果火神在第x分钟单挑这个人(x指单挑完这个人的时间),他就会得到a-b*x的经验值,并且他需要c分钟来打倒这个人. 现在火神想知道,他最多可以得到多少经验值,由于火神本来就很笨,进入zone的疯狂的火神就更笨了,所以他希望你来帮他计算出他最多可以得到多少经验值. 分析 注意到这道题有

【NOIP2016提高A组模拟8.17】(雅礼联考day1)Binary

题目 分析 首先每个数对\(2^i\)取模.也就是把每个数的第i位以后删去. 把它们放进树状数组里面. 那么当查询操作, 答案就位于区间\([2^i-x,2^{i-1}-1-x]\)中,直接查询就可以了. 细节很多,注意处理. #include <cmath> #include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <algor

【NOIP2016提高A组模拟8.17】(雅礼联考day1)Value

题目 分析 易证,最优的答案一定是按\(w_i\)从小到大放. 我们考虑dp, 先将w从小到大排个序,再设\(f_{i,j}\)表示当前做到第i个物品,已选择了j个物品的最大值.转移就是\[f_{i,j}=max\left\{\begin{array}\\f_{i-1,j}\\f_{i-1,j-1}+v_i-w_i*(共选多少个物品(这个要枚举)-j)\end{array}\right.\] 但显然这是\(O(n^3)\)的. 我们考虑如何不用枚举共选多少个物品, 我们考虑反过来做,将w从大到小

【NOIP2016提高A组模拟8.15】Garden

题目 分析 其实原题就是[cqoi2012][bzoj2669]局部极小值. 有一个n行m列的整数矩阵,其中1到nm之间的每个整数恰好出现一次.如果一个格子比所有相邻格子(相邻是指有公共边或公共顶点)都小,我们说这个格子是局部极小值. 给出所有局部极小值的位置,你的任务是判断有多少个可能的矩阵. 发现,X的位置最多有8个,那我们考虑状压dp. 我们从小到大把数填进去,用\(f_{i,j}\)表示,把第i个数填进去后,每个X是否被填了数,用二进制数j表示. 预处理出\(rest_j\)表示填充状态