Codeforces Round #319 (Div. 1) 简要记录

A. Vasya and Petya’s Game

题意:
Vasya和Petya在玩一个游戏。
Vasya在1~n中任意选一个数x,并且Petya要来猜这个数x。
Petya可以进行诸如此类的询问:”x能否被y整除”,Vasya只会回答yes或者no。
询问:
Petya最少需要问几次才能确定数x,并且输出他询问的数都是什么,有多种顺序输出一种即可。
解析:
这显然是个找规律题…
我们发现对于一个能够分出来两个不同质因数的数我们只要对质因数及质因数的几次幂进行询问即可确定该数。
所以我们把目光放在质因数上。
假设我们询问了n以下所有质因数的一次幂,形如 Pi ,则我们一定不能确定 Pi2,P3i,P4i.....
如果紧接着询问了所有质因数的二次幂,形如 P2i ,则我们一定不能确定 Pi3,P4i.......
所以经由上面的规律寻找大概猜测我们要询问所有质因子的x次幂,并且该质因子的x次幂小于等于n。
大概脑补可以想象正确性。
直接暴力搞就行(为了尊重题我写了个线性筛素数23333)
代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 1100
using namespace std;
int n;
int cnt;
int print[N];
int tot;
int prime[N];
int v[N];
void sieve()
{
    for(int i=2;i<=n;i++)
    {
        if(!v[i])
        {
            prime[++tot]=i;
        }
        for(int j=1;j<=tot&&i*prime[j]<=n;j++)
        {
            v[i*prime[j]]=1;
            if(i%prime[j]==0)continue;
        }
    }
}
int main()
{
    scanf("%d",&n);
    sieve();
    for(int i=1;i<=tot;i++)
    {
        int tmp=prime[i];
        while(tmp<=n)
        {
            print[++cnt]=tmp;
            tmp*=prime[i];
        }
    }
    printf("%d\n",cnt);
    for(int i=1;i<=cnt;i++)printf("%d ",print[i]);
    puts("");
}

B. Invariance of Tree

题意:
有一棵大小为n的无向树,我们称一棵树 invariant relative to 一个序列 p=p1p2...pn 当且仅当树上任意一条边 (u,v) ,边 (pu,pv) 也在这棵树上。
询问:
给定序列求该树,不存在则输出NO,否则输出YES并且输出任意一种情况。
解析:
既然是一棵树,那么一定无环,也就是说不能没有大小为1或者2的置换,如果没有的话该树一定存在环,所以我们只需要寻找是否存在大小为1或者2的置换就好了。
如果存在大小为1的置换的话,即把所有其他的点连向它即可。
如果不存在大小为1的置换,反而存在大小为2的置换的话,首先将二者的边连上。
然后枚举其他置换。
我们发现其他置换的奇数点与第一个点连的话,那么所有的奇数点的下一个点都应该与第二个点连。(或者相反:D)
所以如果其他置换中有一个置换的大小为奇数则不能满足题意,输出NO。
否则按照如上方法输出边即可。
如果大小为1,大小为2的置换均不存在直接输出NO即可。
代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 100010
using namespace std;
int n;
int a[N];
int v[N];
int sta[N];
int tag[N];
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    int flag=0;
    for(int i=1;i<=n;i++)
    {
        if(a[i]==i)
        {
            flag=1;
            puts("YES");
            for(int j=1;j<=n;j++)
                if(j!=i)printf("%d %d\n",i,j);
            break;
        }
    }
    if(flag)return 0;
    int tmp=-1;
    for(int i=1;i<=n;i++)
    {
        if(a[a[i]]==i)
        {
            tmp=i;break;
        }
    }
    if(tmp==-1){puts("NO");return 0;}
    v[tmp]=v[a[tmp]]=1;
    for(int i=1;i<=n;i++)
    {
        if(!v[i])
        {
            int top=0;
            while(!v[i])
            {
                sta[++top]=i;
                v[i]=1;
                i=a[i];
            }
            if(top&1){puts("NO");return 0;}
            for(int j=1;j<=top;j++)
            {
                if(j&1)tag[sta[j]]=1;
                else tag[sta[j]]=2;
            }
        }
    }
    puts("YES");
    printf("%d %d\n",tmp,a[tmp]);
    for(int i=1;i<=n;i++)
    {
        if(tag[i]==1)printf("%d %d\n",tmp,i);
        else if(tag[i]==2)printf("%d %d\n",a[tmp],i);
    }
}

C. Points on Plane

题意:
左下角为 [0,0] ,右上角为 [106,106] 的矩阵中有 n 个点。
询问
求一个n个点的序列 P
定义dis(a,b)为点 a 与点b的曼哈顿距离
n1i=1dis(Pi,Pi+1)<=25108
(md点少或者密集的时候不就是随便输出一个排列么2333)
解析
这个 25108 是有内涵的!
我们把这个矩阵切成1000个如下的小矩阵。
这里写图片描述
然后我们将这些小矩阵按照顺序编上号。
所有奇数号内部的点按照纵坐标升序,偶数号内部的点按照纵坐标降序。
之后我们来考虑这种做法的最糟总和。
显然最糟跨越x是 210001000=2106 再多一些来回弹的可能性?所以应该比这个要大一两个数量级。
跨越y最糟是每次内部 109 ,穿越 109
所以大概最糟可以达到 22108 ,完全满足题中要求,我们只需要把点按照这种方式排序输出即可。
代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 1000100
using namespace std;
int n;
struct Point
{
    int x,y,no;
    friend istream& operator >> (istream &_,Point &a)
    {scanf("%d%d",&a.x,&a.y);return _;}
}pt[N];

int get_dis(Point a,Point b)
{
    return abs(a.x-b.x)+abs(a.y-b.y);
}
bool cmp(Point a,Point b)
{
    return a.x<b.x;
}
bool cmp2(Point a,Point b)
{
    return a.y<b.y;
}
bool cmp3(Point a,Point b)
{
    return a.y>b.y;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++){cin>>pt[i];pt[i].no=i;}
    sort(pt+1,pt+n+1,cmp);
    int la=0,flag=1,lano=0;
    for(int i=1;i<=n;i++)
    {
        if(pt[i].x-la>=1000)
        {
            if(flag)
                sort(pt+lano+1,pt+i+1,cmp2);
            else sort(pt+lano+1,pt+i+1,cmp3);
            lano=i;
            flag^=1;
            la+=1000;
        }
    }
    for(int i=1;i<=n;i++)printf("%d ",pt[i].no);
    puts("");
}

D. Flights for Regular Customers

题意:
n 个点,m有向边,每条边有3个信息,起点,终点以及一个最小经过等级d。
我们能经过一条边的先决条件是我们之前已经走过了至少d条边。
2n150,1m150,0di109
询问:
1到n最少经过多少条边。
解析:
由于d很大,所以我们不能用?分层图?的想法强行搞过去。
但是我们观察到m的范围相当的小,也就是说,我们的对应的连边邻接矩阵最多也就是150个。
所以我们考虑是否可以按照边的di值升序排序,之后每一次转移一次 dis 矩阵?
发现这种思路似乎可行。
于是我们设 dis[i][j] 表示i到j最少经过多少条边。
如果当前有一个01矩阵代表走 di 条边的连通性情况。
然后我们扫到第 i+1 条边的时候。
显然是走一次的矩阵的( di+1di )次幂乘以我们走 di 条边的连通性即可。(注意弄完之后连通性矩阵仍然是01的:D),至于 dis ,我们可以在枚举m的情况下用 floyd 更新即可。
所以复杂度大概是 O(n2mlog2d) ,出题人说过不了
难道前功尽弃了嘛?(但是我感觉能过,出题人好像算错复杂度了)
并没有,因为这个矩阵始终是一个01矩阵,所以可以上bitset优化嘛23333
复杂度直接变成O(n^2*m*log_2d/32)
代码:

#include <cstdio>
#include <bitset>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 160
using namespace std;
bitset<N>use[N],base[N],tmp[N];
int dis[N][N];
int n,m; 
struct Line
{
    int x,y,d;
}edge[N];
bool cmp(Line a,Line b)
{
    return a.d<b.d;
}
void mul(bitset<N>*a,bitset<N>*b)
{
    bitset<N>ret[N];
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            if(a[i][j])ret[i]|=b[j];
    for(int i=1;i<=n;i++)a[i]=ret[i];
}
void Quick_Power(bitset<N>*a,int b)
{
    bitset<N>ret[N];
    for(int i=1;i<=n;i++)ret[i][i]=1;
    while(b)
    {
        if(b&1)mul(ret,a);
        mul(a,a);
        b>>=1;
    }
    for(int i=1;i<=n;i++)a[i]=ret[i];
}
int ans;
int main()
{
    ans=0x3f3f3f3f;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int x,y,d;
        scanf("%d%d%d",&x,&y,&d);
        edge[i].x=x,edge[i].y=y,edge[i].d=d;
    }
    sort(edge+1,edge+m+1,cmp);
    memset(dis,0x3f,sizeof(dis));
    for(int i=1;i<=n;i++)use[i][i]=1,dis[i][i]=0; 
    int la=0;
    for(int i=1;i<=m;i++)
    {
        int x=edge[i].x,y=edge[i].y,d=edge[i].d;
        for(int j=1;j<=n;j++)
            for(int k=1;k<=n;k++)
                dis[j][k]=min(dis[j][x]+1+dis[y][k],dis[j][k]);
        for(int j=1;j<=n;j++)tmp[j]=base[j];
        Quick_Power(tmp,d-la);
        mul(use,tmp);
        for(int j=1;j<n;j++)
        {
            if(use[1][j])
            {
                if(dis[j][n]!=0x3f3f3f3f)
                    ans=min(ans,d+dis[j][n]);
            }
        }
        la=d;
        base[x][y]=1;
    }
    if(ans==0x3f3f3f3f)puts("Impossible");
    else printf("%d\n",ans);
} 

E

去看PoPoQQQ题解吧,bzoj4025好像是这个的弱化,然而我是用lct强行搞过去的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值