FR #2题解

A.

  考虑把(u,v)的询问离线挂在u上,然后dfs,每次从fath[x]到[x]相当于x子树dis区间加1,x子树以外区间-1,然后维护区间和区间平方和等。

常数略大。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define maxv 100500
#define maxe 200500
using namespace std;
long long n,fath[maxv],q,uu,vv,dis[maxv],l[maxv],r[maxv],times=0,fdfn[maxv],g[maxv],nume=1,ans[maxv],cnt;
long long tot=0,root,ls[maxv<<2],rs[maxv<<2],val1[maxv<<2],val2[maxv<<2],lazy[maxv<<2];
vector <long long> v[maxv],id[maxv];
bool vis[maxv];
struct edge
{
    long long v,nxt;
}e[maxe];
long long read()
{
    char ch;long long data=0;
    while (ch<‘0‘ || ch>‘9‘) ch=getchar();
    while (ch>=‘0‘ && ch<=‘9‘)
    {
        data=data*10+ch-‘0‘;
        ch=getchar();
    }
    return data;
}
void addedge(long long u,long long v)
{
    e[++nume].v=v;e[nume].nxt=g[u];g[u]=nume;
    e[++nume].v=u;e[nume].nxt=g[v];g[v]=nume;
}
void pushup(long long now,long long left,long long right)
{
    val1[now]=val1[ls[now]]+val1[rs[now]];
    val2[now]=val2[ls[now]]+val2[rs[now]];
}
void pushdown(long long now,long long left,long long right)
{
    if (!lazy[now]) return;
    long long mid=left+right>>1,d=lazy[now],ll=mid-left+1,rr=right-mid;
    val1[ls[now]]+=2*d*val2[ls[now]]+d*d*ll;val1[rs[now]]+=2*d*val2[rs[now]]+d*d*rr;
    val2[ls[now]]+=d*ll;val2[rs[now]]+=d*rr;
    lazy[ls[now]]+=d;lazy[rs[now]]+=d;
    lazy[now]=0;
}
void build(long long &now,long long left,long long right)
{
    now=++tot;lazy[now]=0;
    if (left==right)
    {
        val1[now]=dis[fdfn[left]]*dis[fdfn[left]];val2[now]=dis[fdfn[left]];
        return;
    }
    long long mid=left+right>>1;
    build(ls[now],left,mid);
    build(rs[now],mid+1,right);
    pushup(now,left,right);
}
long long ask(long long now,long long left,long long right,long long l,long long r)
{
    pushdown(now,left,right);
    if ((left==l) && (right==r)) return val1[now];
    long long mid=left+right>>1;
    if (r<=mid) return ask(ls[now],left,mid,l,r);
    else if (l>=mid+1) return ask(rs[now],mid+1,right,l,r);
    else return ask(ls[now],left,mid,l,mid)+ask(rs[now],mid+1,right,mid+1,r);
}
void modify(long long now,long long left,long long right,long long l,long long r,long long val)
{
    if (l>r) return;
    pushdown(now,left,right);
    if ((left==l) && (right==r))
    {
        lazy[now]+=val;
        val1[now]+=2*val*val2[now]+val*val*(right-left+1);val2[now]+=val*(right-left+1);
        return;
    }
    long long mid=left+right>>1;
    if (r<=mid) modify(ls[now],left,mid,l,r,val);
    else if (l>=mid+1) modify(rs[now],mid+1,right,l,r,val);
    else
    {
        modify(ls[now],left,mid,l,mid,val);
        modify(rs[now],mid+1,right,mid+1,r,val);
    }
    pushup(now,left,right);
}
void get_ans(long long x)
{
    for (long long i=0;i<v[x].size();i++)
        ans[id[x][i]]=ask(root,1,n,l[v[x][i]],r[v[x][i]]);
}
void modify_tree(long long x,long long f)
{
    modify(root,1,n,l[x],r[x],-f);
    modify(root,1,n,1,l[x]-1,f);
    modify(root,1,n,r[x]+1,n,f);
}
void dfs1(long long x)
{
    l[x]=r[x]=++times;fdfn[times]=x;
    for (long long i=g[x];i;i=e[i].nxt)
    {
        long long v=e[i].v;
        if (v!=fath[x])
        {
            dis[v]=dis[x]+1;
            dfs1(v);
            r[x]=max(r[x],r[v]);
        }
    }
}
void dfs2(long long x)
{
    get_ans(x);
    for (long long i=g[x];i;i=e[i].nxt)
    {
        long long v=e[i].v;
        if (v!=fath[x])
        {
            modify_tree(v,1);
            dfs2(v);
            modify_tree(v,-1);
        }
    }
}
int main()
{
    n=read();
    for (long long i=1;i<=n-1;i++) {fath[i+1]=read();addedge(fath[i+1],i+1);}
    q=read();
    for (long long i=1;i<=q;i++)
    {
        uu=read();vv=read();
        v[uu].push_back(vv);id[uu].push_back(i);
    }
    dfs1(1);build(root,1,n);
    dfs2(1);
    for (long long i=1;i<=q;i++) printf("%lld\n",ans[i]);
    return 0;
}

B.

  我们发现k=0的时候可以o(1)计算(毕竟是01序列)。当k>=1的时候可以证明答案是ceil(l/2)*trunc(l/2)。

要善于猜结论啊。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 500500
using namespace std;
long long n,q,a[maxn],s1[maxn],s[maxn],x[maxn],y[maxn],k[maxn],cnt[2][maxn],ans[maxn],tab[maxn];
long long read()
{
    char ch;long long data=0;
    while (ch<‘0‘ || ch>‘9‘) ch=getchar();
    while (ch>=‘0‘ && ch<=‘9‘)
    {
        data=data*10+ch-‘0‘;
        ch=getchar();
    }
    return data;
}
void calc(long long type,long long pos)
{
    cnt[0][0]=1;tab[0]=0;
    for (long long i=1;i<=n;i++)
    {
        cnt[0][i]=cnt[0][i-1];cnt[1][i]=cnt[1][i-1];
        if (!s[i]) cnt[0][i]++;
        else cnt[1][i]++;
        tab[i]=tab[i-1]+cnt[s[i]^1][i];
    }
    if (!type)
    {
        for (long long i=1;i<=n;i++)
        {
            if (x[i]>=2) ans[i]=(cnt[1][y[i]-1]-cnt[1][x[i]-2])*cnt[0][y[i]]+(cnt[0][y[i]-1]-cnt[0][x[i]-2])*cnt[1][y[i]]-(tab[y[i]-1]-tab[x[i]-2]);
            else ans[i]=cnt[1][y[i]-1]*cnt[0][y[i]]+cnt[0][y[i]-1]*cnt[1][y[i]]-tab[y[i]-1];
        }
    }
}
int main()
{
    n=read();q=read();
    for (long long i=1;i<=n;i++) {a[i]=read();s1[i]=s1[i-1]^a[i];s[i]=s1[i];}
    for (long long i=1;i<=q;i++) {x[i]=read();y[i]=read();k[i]=read();x[i]++;y[i]++;}
    calc(0,0);
    for (int i=1;i<=q;i++)
    {
        if (k[i])
        {
            long long l=y[i]-x[i]+2;
            ans[i]=(l/2+(l&1))*(l/2);
        }
    }
    for (long long i=1;i<=q;i++) printf("%lld\n",ans[i]);
    return 0;
}

C.

  这TM的是个环套树啊。、

我们找出路径上的值,然后每次加gcd(环长,m)加到最大即可。

之前10分的原因是神TM没开long long。(我就说我怎么可能写挂这种题)(撤回)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxv 100500
#define maxe 200500
using namespace std;
struct edge
{
    long long v,w,nxt;
}e[maxe];
long long n,g[maxv],nume=1,dis1[maxv],dis2[maxv],root,father[maxv],anc[maxv][20],dep[maxv];
long long r1,r2,q,s,t,m,u,v,w,a,b,x;
long long read()
{
    char ch;long long data=0;
    while (ch<‘0‘ || ch>‘9‘) ch=getchar();
    while (ch>=‘0‘ && ch<=‘9‘)
    {
        data=data*10+ch-‘0‘;
        ch=getchar();
    }
    return data;
}
void addedge(long long u,long long v,long long w) {e[++nume].v=v;e[nume].w=w;e[nume].nxt=g[u];g[u]=nume;}
long long getfather(long long x)
{
    if (x!=father[x]) father[x]=getfather(father[x]);
    return father[x];
}
bool unionn(long long a,long long b)
{
    long long f1=getfather(a),f2=getfather(b);
    if (f1==f2) return true;
    father[f1]=f2;return false;
}
void dfs(long long x,long long father)
{
    for (long long i=g[x];i;i=e[i].nxt)
    {
        long long v=e[i].v;
        if (v!=father)
        {
            anc[v][0]=x;dep[v]=dep[x]+1;
            dis1[v]=dis1[x]+e[i].w;dis2[v]=dis2[x]+e[i^1].w;
            dfs(v,x);
        }
    }
}
void get_table()
{
    for (long long e=1;e<=19;e++)
        for (long long i=1;i<=n;i++)
            anc[i][e]=anc[anc[i][e-1]][e-1];
}
long long lca(long long x,long long y)
{
    if (dep[x]<dep[y]) swap(x,y);
    for (long long e=19;e>=0;e--)
        if ((dep[anc[x][e]]>=dep[y]) && (anc[x][e]))
            x=anc[x][e];
    if (x==y) return x;
    for (long long e=19;e>=0;e--)
    {
        if (anc[x][e]!=anc[y][e])
        {
            x=anc[x][e];
            y=anc[y][e];
        }
    }
    return anc[x][0];
}
long long gcd(long long a,long long b)
{
    if (!b) return a;
    return gcd(b,a%b);
}
long long calc(long long x)
{
    x=(x%m+m)%m;
    long long f1=(r1%m+m)%m,d1=gcd(f1,m);
    return (m-1-x)/d1*d1+x;
}
int main()
{
    n=read();
    for (long long i=1;i<=n;i++) father[i]=i;
    for (long long i=1;i<=n;i++)
    {
        a=read();b=read();x=read();
        if (unionn(a,b)) {root=a;u=a;v=b;w=x;}
        else {addedge(a,b,x);addedge(b,a,-x);}
    }
    dfs(root,0);get_table();
    r1=dis1[v]-w;r2=dis2[v]+w;
    q=read();
    for (long long i=1;i<=q;i++)
    {
        s=read();t=read();m=read();x=lca(s,t);
        printf("%lld\n",calc(dis2[s]-dis2[x]+dis1[t]-dis1[x]));
    }
    return 0;
}
时间: 2024-09-30 14:53:44

FR #2题解的相关文章

FR #10题解

好 蠢 啊 A. 标准分治.每次从分治区间中找到最大值的位置m,设f[l,r]为[l,r]的答案,那么f[l,r]=f[l,m-1]+f[m+1,r]+跨过m点的贡献. 然后枚举小的区间放到大的区间中查就行了.复杂度nlog^2n. TM的这5e5你给128M怎么回事...开6s又怎么回事... #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define

FR #1题解

A. 建图跑最小费用最大流.分类讨论每种情况如何连边,费用怎么定. #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> #define maxv 105 #define maxe 100500 #define inf 1000000000 using namespace std; int n,m,a[maxv],b[ma

FR #11题解

A. 瞎jb折半dp一下,然后合并一下就好了. O2加成十分显著...6s变成0.9s... #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<bitset> #define maxv 100 #define maxe 20000 using namespace std; int n,m,d,x,y,z,g[maxv],nume=1

FR #12题解

A. 我的做法是nmlogn的....直接做m次堆贪心就可以.按理说是能过的... 正解直接是在原dp上搞一搞...可以做到n^2+nlog? 2333 #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> #define maxn 2050 using namespace std; long long n,m,a[max

bzoj 3519: [Zjoi2014] 消棋子 题解

[序言]在大家怀疑的眼光下,我做了一个中午和半个下午.调了一个晚上的题目总算A了! [原题] 消棋子是一个有趣的游戏.游戏在一个r * c的棋盘上进行.棋盘的每个格 子,要么是空,要么是一种颜色的棋子.同一种颜色的棋子恰好有两个.每一轮, 玩家可以选择一个空格子(x, y),并选择上下左右四个方向中的两个方向,如果 在这两个方向上均存在有棋子的格子,而且沿着这两个方向上第一个遇到的棋子 颜色相同,那么,我们将这两个棋子拿走,并称之为合法的操作.否则称这个操 作不合法,游戏不会处理这个操作.游戏的

东南大学第十三届程序设计竞赛初赛题解

问题 A: 天梯评分系统 题目描述 在一个下雨的日子,沈学姐和四个好基友约定无事一同打dota(dota是一个5对5的MOBA类游戏)因为想证明谁最NB,他们就全部注册新号去爬天梯了.天梯有一套完整的评分系统,它可以根据每位选手每局的数据进行评分,因为dota的英雄既有辅助又有ganker还有后期,所以不同的英雄的评分标准不一样.可惜那天天梯服务器维护,无法进行评分.于是,他们记录下每一局的数据,找你来帮忙,希望你能够帮他们仿照天梯编一个评分系统,以便于他们比较谁是真正的神牛. 已知对于每个账号

LeetCode: Roman to Interger 题解

Given a roman numeral, convert it to an integer. Input is guaranteed to be within the range from 1 to 3999. 找到规则即可 罗马数字的表示: I~1 V~5 X~10 L~50 C~100 D~500 M~1000 规则: 基本数字Ⅰ.X .C 中的任何一个,自身连用构成数目,或者放在大数的右边连用构成数目,都不能超过三个:放在大数的左边只能用一个. 不能把基本数字V .L .D 中的任何一

codeforces A. Shaass and Oskols 题解

Shaass has decided to hunt some birds. There are n horizontal electricity wires aligned parallel to each other. Wires are numbered 1 to n from top to bottom. On each wire there are some oskols sitting next to each other. Oskol is the name of a delici

2016 年宁波工程学院第七届ACM校赛题解报告

2016 年宁波工程学院第七届ACM校赛题解报告 本题解代码直接为比赛代码,仅供参考. A,B,C,D,G,H,J,K,L,M 来自 Ticsmtc 同学. F 来自 Gealo 同学. E,I 来自Alex 学长. Promblem A :    Two Sum 时间限制: 1 Sec  内存限制: 64 MB 题目描述: 给出n个数,另外给出?个整数S,判断是否可以从中取出2个数,使得这两个数的和是S. 输入: 第?行有个整数T(1 <= T <= 10),代表数据组数. 对于每组数据,第