码迷,mamicode.com
首页 > 其他好文 > 详细

1006

时间:2019-10-06 16:55:13      阅读:135      评论:0      收藏:0      [点我收藏+]

标签:条件   code   sdi   形式   efi   inline   strong   如何   数学   

hh A题算了下空间感觉没问题 没想到开了4个数组 成功bao 0
期望290 实际 175
naiive

A.劳伦斯数
数学家劳伦斯定义: 对于任意整数 x, 如果 x 是素数, 或者 x 可以表示成两个素数的乘积, 那
么 x 就称为劳伦斯数。
现在给出 Q 个询问, 每个询问形如“L R” , 需要你快速统计出区间[L,R]中有多少个劳伦斯
数。
解:
这题肯定和线性筛有关系
然后打了一波板子后 发现 对于x 表示成两个素数的乘积的形式 我们可以这样想想
当i是一个质数 那么i*pri[j] 就会满足条件 并且由于线性筛的特性 pri[j] 一定是一个最小的质因数

就像这样

    if(phi[le]==i)
            {
                p[i*phi[j]]=1;
            }

或者定义pr[x] 表示x 的最小质因数
那么充要条件为 pr[i/pr[i]]=i/pr[i]; 即最小的质因数是自己就满足素数的条件

考完后我发现自己还是太naiive
看到可能int 溢出不知道强制类型转换 省掉空间
看到标记的数组还开long long
1e9+1 搞定的事情 自己非要数着写

B.逃离迷宫
文件名: escape.cpp
输入输出名: escape.in/escape.out
时空限制 1s, 256m
题目描述
队员们在玩一款迷宫游戏。 在一个山体迷宫中, 共有 N 个山洞, 编号 1 到 N。
游戏开始时, 队员们位于 1 号山洞, 出口在 N 号山洞。 每个山洞里面可能有一些通行卡, 进入
一个山洞, 队员们就能获得该山洞的通行卡。 通行卡有 K 种, 编号 1 到 K。 山洞之间存在单向道路,
每条道路都需要拥有指定编号的通行卡才能通过。
通过一条道路的时间是 1 分钟, 队员们想知道, 最少多少分钟, 就能走到出口。
输入格式:
第一行, 三个整数 N,M,K, 表示山洞数、 道路数和通行卡种数
接下来 K 行, 其中第 i 行描述 i 号山洞存在的通行卡的情况, 用 N 个空格间隔的 0 或 1 来表
示。 若其中第 j 个数为 1, 表示该山洞存在 j 号通行卡, 为 0 则表示不存在第 j 号通行卡。
接下来 M 行, 每行描述一条道路的情况。 首先是两个数 X 和 Y, 表示有一条从 X 出发到达 Y
的通道。 接下来 K 空格间隔的个 0 或 1, 表示通过该通道需要拥有的通行卡的情况, 若其中第 j 个
数为 1, 表示需要 j 号通行卡, 为 0 则表示不需要。
输出格式:
一个整数, 表示最少所需时间。 若无法到达出口, 输出“No Solution” 。
样例输入:
3 3 2
1 0
0 1
0 0
1 3 1 1
1 2 1 0
2 3 1 1
样例输出:
2

解:
多维最短路+状压DP
定义f[i][j] 表示 在i 号点 集合为j 所需要的最小时间
满足条件: $ ((r.s$ & $ le[i])==le[i])$
转移为: $
if(dis[r.f][r.s]+1<dis[u][(r.s)|w[u]])
{
dis[u][(r.s)|w[u]]=dis[r.f][r.s]+1,
if(!mark[u][(r.s)|w[u]])
{
mark[u][(r.s)|w[u]]=1;
}
}
$

代码:

//
#include<bits/stdc++.h>
using namespace std;
#define maxnn 6010
int mark[maxnn][1<<11];
int n,m,k;
int all=0;
int las[maxnn],en[maxnn],le[maxnn],tot,nex[maxnn];
int dis[maxnn][1<<11];
int w[maxnn];
typedef pair<int ,int > P;
#define f first 
#define s second
#define inf 100000000
void add(int a ,int b,int c)
{
    en[++tot]=b;
    nex[tot]=las[a];
    las[a]=tot;
    le[tot]=c;
}
void spfa()
{
    queue<P> Q;
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<=all;j++){
            dis[i][j]=inf;
            mark[i][j]=0;
        }
    }
    dis[1][w[1]]=0;
    mark[1][w[1]]=1;
    Q.push(make_pair(1,w[1]));
    while(Q.size())
    {
        P r=Q.front();
        Q.pop();
        mark[r.f][r.s]=0;
        for(int i=las[r.f];i;i=nex[i])
        {
            int u=en[i];
            if(!((r.s&le[i])==le[i]))continue;
            if(dis[r.f][r.s]+1<dis[u][(r.s)|w[u]])
            {
                dis[u][(r.s)|w[u]]=dis[r.f][r.s]+1;
                if(!mark[u][(r.s)|w[u]])
                {
                    mark[u][(r.s)|w[u]]=1;
                    Q.push(make_pair(u,(r.s)|w[u]));
                }
            }
        }
    }
}

void FIE()
{
    freopen("escape.in","r" ,stdin);
    freopen("escape.out","w",stdout);
}
int main()
{
    //FIE();
    int x,y,z;
    scanf("%d%d%d",&n,&m,&k);
    all=(1<<k)-1;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=k;j++){
            scanf("%d",&z);
            if(z==1)
            w[i]=w[i]|((1<<j-1));
        }
    }
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&x,&y);
        int tmp=0;
        for(int j=1;j<=k;j++){
            cin>>z;
            if(z==1)
            tmp=tmp|((1<<j-1));
        }
        add(x,y,tmp);
    }
    spfa();
    int ans=100000000;
    for(int i=0;i<=all;i++){
        ans=min(ans,dis[n][i]);
    }
    if(ans==100000000)
    {
        puts("No Solution");
    }
    else
    printf("%d",ans);
}

C 线路重叠
某城市有非常发达的公共交通系统。 有 N 个公交站, 通过 N-1 条双向道路连接起来。 公交站编
号 1 到 N, 任意两个公交站都可以相互到达。
队员们提出 Q 个询问, 每个询问形如“X Z Y” , 表示问从 X 到 Z 的公交线路, 与从 Y 到 Z 的
公线路, 存在多少个重叠的车站。 重叠指车站既在 X 到 Z 路线中, 也在 Y 到 Z 路线中。
输入格式:
第一行, 两个整数 N 和 Q, 表示询问数
接下来 N-1 行, 每行两个整数 X 和 Y, 表示编号 X 和 Y 的公交站存在道路相连。
接下来 Q 行, 每行三个整数 X,Z,Y, 表示一次询问
输出格式:
Q 行, 每行一个整数, 对应一次询问的答案
样例输入:
3 3
1 2
2 3
1 2 3
1 1 3
3 1 3
样例输出:
1 1 3

解:
考场上没有注意到这是一个固定终点的问题 敲了一发树剖 T 75 旁边的某位ruan姓jul 打的比我复杂T90 我... 充分证明了fread 的重要性 时间复杂度\(O(Q \times (log2(n))^2)\)
考试后 利用树状数组优化 还是T90...
树剖的想法很好想
对于一段距离 在这条路径上+1 对于另一段距离 在他的路径上查询区间和 然后删去
发一波我写的树状数组的树剖

\((k+1) \sum c[i] - \sum x*c[i]\)
x 与 k 均为 add和get 函数里面 i的初始值

inline void add(int a ,int b) {
    en[++tot]=b;
    nex[tot]=las[a];
    las[a]=tot;
}
inline void ad1(int x,int d)
{
    for(int i=x;i<=n+100;i+=lowbit(i))
    {
        c1[i]+=d;
    }
}
inline void ad2(int x,int d)
{
    for(int i=x;i<=n+100;i+=lowbit(i))
    {
        c2[i]=c2[i]+x*d;
    }
}
inline int get1(int x)
{
    int ans=0;
    for(int i=x;i;i-=lowbit(i))
    {
        ans=ans+(x+1)*c1[i];
    }
    return ans;
}
inline int get2(int x)
{
    int ans=0;
    for(int i=x;i;i-=lowbit(i))
    {
        ans+=c2[i];
    }
    return ans;
}
//

#include<bits/stdc++.h>
using namespace std;
#define maxnn 1000000
#define maxn 500100
#define ll long long
#define lowbit(i) i&(-i) 
char pbuf[1<<20],*pf=pbuf;
inline void push(char c)
{
    if(pf-pbuf==(1<<20))fwrite(pbuf,1,1<<20,stdout),pf=pbuf;
    *pf++=c;
}
template<typename T>
inline void print(T x)
{
    static short sta[35];
    if(x<0)push('-'),x=-x;
    int TOP=0;
    do{sta[TOP++]=x%10;x/=10;}while(x);
    while(TOP)push('0'+sta[--TOP]);
    push('\n');
}
char buf[1<<20],*p1,*p2;
#define GC (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?0:*p1++)
inline int R()
{
    char t=GC;
    int x=0;
    while(!isdigit(t)) t=GC;
    while(isdigit(t)) x=x*10+t-48,t=GC;
    return x;
}
int c1[maxnn],c2[maxnn];
int las[maxn],en[maxn],tot,nex[maxn];
int n,Q;
inline void add(int a ,int b) {
    en[++tot]=b;
    nex[tot]=las[a];
    las[a]=tot;
}
inline void ad1(int x,int d)
{
    for(int i=x;i<=n+100;i+=lowbit(i))
    {
        c1[i]+=d;
    }
}
inline void ad2(int x,int d)
{
    for(int i=x;i<=n+100;i+=lowbit(i))
    {
        c2[i]=c2[i]+x*d;
    }
}
inline int get1(int x)
{
    int ans=0;
    for(int i=x;i;i-=lowbit(i))
    {
        ans=ans+(x+1)*c1[i];
    }
    return ans;
}
inline int get2(int x)
{
    int ans=0;
    for(int i=x;i;i-=lowbit(i))
    {
        ans+=c2[i];
    }
    return ans;
}
int f[maxn][20];
int dfn[maxnn],cnt;
inline int go_up(int s,ll k) {
    int d=log2(n);
    for(int i=d; i>=0; i--) {
        if(k&(1<<i)) {
            s=f[s][i];
        }
    }
    return s;
}
ll dep[maxnn];
int topp[maxnn];
int son[maxn];
inline int lca(int x,int y) {
    if(dep[x]<dep[y])
        swap(x,y);
    int s=dep[x]-dep[y];
    x=go_up(x,s);
    if(x==y) return y;
    s=log2(n);
    for(int i=s; i>=0; i--) {
        if(f[x][i]!=f[y][i]) {
            x=f[x][i];
            y=f[y][i];
        }
    }
    return f[y][0];
}
ll size[maxn];
inline void dfs1(int v,int fa) {
    int maxson=-1;
    f[v][0]=fa;
    dep[v]=dep[fa]+1;
    int s=log2(n);
    for(int i=1; i<=s; i++) {
        f[v][i]=f[f[v][i-1]][i-1];
    }
    size[v]=1;
    for(int i=las[v]; i; i=nex[i]) {
        int u=en[i];
        if(u!=fa) {
            dfs1(u,v);
            size[v]+=size[u];
            if(size[u]>maxson)
            {
                maxson=size[u];
                son[v]=u;
            }
        }
    }
}
inline void dfs2(int v,int fa,int t) {
    topp[v]=t;
    dfn[v]=++cnt;
    if(son[v]) dfs2(son[v],v,t);
    for(int i=las[v]; i; i=nex[i]) {
        int u=en[i];
        if(u!=fa&&u!=son[v]) {
            dfs2(u,v,u);
        }
    }
}
inline void add1(int x,int y,int d)
{
    while(topp[x]!=topp[y]){
        if(dep[topp[x]]<dep[topp[y]])
        {
            swap(x,y);
        }
        ad1(dfn[topp[x]],d);
        ad1(dfn[x]+1,-d);
        ad2(dfn[topp[x]],d);
        ad2(dfn[x]+1,-d);
        x=f[topp[x]][0];
    }
    if(dep[x]<dep[y])
        {
            swap(x,y);
        }
    ad1(dfn[y],d);
    ad1(dfn[x]+1,-d);
    ad2(dfn[y],d);
    ad2(dfn[x]+1,-d);
}
inline int get(int x,int y)
{
    int ans=0;
    while(topp[x]!=topp[y]){
        if(dep[topp[x]]<dep[topp[y]])
        {
            swap(x,y);
        }
        int tmp1=dfn[topp[x]];
        int tmp2=dfn[x];
        ans=ans+(get1(tmp2)-get2(tmp2)-(get1(tmp1-1)-get2(tmp1-1)));
        x=f[topp[x]][0];
    }
        if(dep[x]<dep[y])
        {
            swap(x,y);
        }
        int tmp1=dfn[y];
        int tmp2=dfn[x];
        ans=ans+(get1(tmp2)-get2(tmp2)-(get1(tmp1-1)-get2(tmp1-1)));
        return ans;
}
inline void ch1(int x,int y) {
    if(dep[x]<dep[y]) swap(x,y);
    int now=lca(x,y);
    if(now!=y) {
        add1(now,now,1);
        int tmp1=dep[x]-dep[now];
        int id1=go_up(x,tmp1-1);
        add1(id1,x,1);
        tmp1=dep[y]-dep[now];
        int id2=go_up(y,tmp1-1);
        add1(id2,y,1);
    } else {
        add1(y,x,1);
    }
}
inline ll ch2(int x,int y) {
    ll ans=0;
    if(dep[x]<dep[y]) swap(x,y);
    int now=lca(x,y);
    if(now!=y) {
        ans+=get(now,now);
        int tmp1=dep[x]-dep[now];
        int id1=go_up(x,tmp1-1);
        ans+=get(id1,x);
        tmp1=dep[y]-dep[now];
        int id2=go_up(y,tmp1-1);
        ans+=get(id2,y);
    } else {
        ans+=get(y,x);
    }
    return ans;
}
inline void era(int x,int y) {
    if(dep[x]<dep[y]) swap(x,y);
    int now=lca(x,y);
    if(now!=y) {
        add1(now,now,-1);
        int tmp1=dep[x]-dep[now];
        int id1=go_up(x,tmp1-1);
        add1(id1,x,-1);
        tmp1=dep[y]-dep[now];
        int id2=go_up(y,tmp1-1);
        add1(id2,y,-1);
    } else {
        add1(y,x,-1);
    }
}
int u;
void FIE()
{
    freopen("overlap1.in","r" ,stdin);
    freopen("op.out","w",stdout);
}
int main() {
    //FIE();
    n=R();
    Q=R();
    u=R();
    int x,y,z;
    for(int i=1; i<n; i++) {
        x=R();
        y=R();
        add(x,y);
        add(y,x);
    }
    dfs1(1,0);
    dfs2(1,0,1);
    while(Q--) {
        x=R();
        z=R();
        y=R();
        ch1(x,z);
        int ans=ch2(y,z);
        print(ans);
        era(x,z);
    }   
 fwrite(pbuf,1,pf-pbuf,stdout);
}

下面是倍增的想法:
注意到终点是固定的
那么也就是说x或者y或者z 应该是与另一个点 到一个汇点 然后再一起到终点的

技术图片

官方题解:
问题实际上是问树上一个点到另外两点路径的重合长度
树上三点,或是呈一条链的形式,或是存在一个中心,使得中心到三点之间的路径不相交。如下图,上方的两种情况是A、B、C点存在中心O,下方两种情况是A、B、C点在一条链上。对于呈一条链的形式,我们不妨将三个点中,位于中间的那个点称为三个点的中心,如此一来,我们要求的答案实际上是中心到B点的距离。那么,如何求中心呢?通过观察上图,我们发现,中心一定是A、B、C点中两点的LCA。并且,通过进一步观察,我们可以发现,中心是A、B、C点两两的LCA中,深度最大的点。所以,我们只需求出中心,在求出中心到B点的距离即可解决本题。该算法的时间复杂度是O(N+Q)或O((N+Q)*LogN)的。

这题很像紧急集合那道题
询问树上的一个点到其他三个点的距离之和最短
两两lca 深度最大的即为答案

或者也可以分类讨论
假如z在xy与lca的路径之外 答案为lca到z的路径距离
假如z在xy与lca的路径之中 答案为lca到z的路径距离 答案为在这条路径上的点与z的lca到z的距离 可以通过dfs序列和倍增来实现

读题
文件
心态
空间
时间分配

1006

标签:条件   code   sdi   形式   efi   inline   strong   如何   数学   

原文地址:https://www.cnblogs.com/OIEREDSION/p/11627555.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!