8.24模拟赛

2 篇文章 0 订阅
2 篇文章 0 订阅

T1
豪迈 (heroic)
来自aqx的提示:这题真的很简单,别慌,我一眼秒了。
【问题描述】
ranwen特别喜欢数列和树,他觉得它们是世界上最美妙的事物。
有一天, 某人给了ranwen一个长度为N的整数数列a。这让ranwen特别想构造一棵豪迈树。
豪迈树的每条边长度都为1。而且豪迈树有一个最重要的性质:对于每一个点i(1<=i<=N),在树中离它距离最远的点与它的距离应恰好等于ai。
ranwen想了想就秒掉了这题,他决定考考你:对于一个给定的序列,是否存在一棵豪迈树?
【输入格式】
从文件heroic.in中读入数据。
第一行T;之后对于每组数据:
第一行n;
接着一行n个数表示序列。
【输出格式】
输出到文件heroic.out中。
T行,如果对于输入的序列存在豪迈树,则输出”Possible”,否则输出”Impossible”。二者皆不含引号。
【样例输入】
6
5
3 2 2 3 3
3
1 1 2
10
1 2 2 2 2 2 2 2 2 2
10
1 1 2 2 2 2 2 2 2 2
6
1 1 1 1 1 5
5
4 3 2 3 4
【样例输出】
Possible
Impossible
Possible
Impossible
Impossible
Possible
【数据范围】
所有的数字都表示小于等于。
编号 n
1-3 10
4-6 100
7-15 1000
16-20 100000
T<=5 ai

#include<stdio.h>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<bitset>
#include<vector>
#include<math.h>
using namespace std;
int n;
int a[100005],cnt[100005];
int main()
{
    freopen("heroic.in","r",stdin);
    freopen("heroic.out","w",stdout);
    int t;
    scanf("%d",&t);
    while(t--)
    {
        memset(cnt,0,sizeof(cnt));
        scanf("%d",&n);
        int mx=0,mn=99999999;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            cnt[a[i]]++;
            mx=max(mx,a[i]);
            mn=min(mn,a[i]);
        }
        //cout<<cnt[2]<<' '<<cnt[3]<<endl;
        if(mx&1)
        {
            int flag=1;
            for(int i=mx;i>=((mx+1)>>1);i--)
            {
                if(cnt[i]<2)
                {
                    flag=0;
                    break;
                }
            }
            if(mn<((mx+1)>>1))flag=0;
            //cout<<flag<<endl;
            if(cnt[(mx+1)>>1]>2)flag=0;
            if(flag)printf("Possible\n");
            else printf("Impossible\n");
        }
        else
        {
            int flag=1;
            for(int i=mx;i>(mx>>1);i--)
            {
                if(cnt[i]<2)
                {
                    flag=0;
                    break;
                }
            }
            if(mn<(mx>>1))flag=0;
            if(cnt[mx>>1]!=1)flag=0;
            if(flag)printf("Possible\n");
            else printf("Impossible\n");
        }
    }
}

T2
ranwen
来自aqx的提示:考虑一下差分思想,把题目转化一下。
【问题描述】
有无限多的ranwen,编号为1,2,3,…。开始时,第x1,x2,…,xN个ranwen是躺着的,其它的ranwen是趴着的
你可以进行一些操作,每个操作大概是这样的:
选择一个不小于3的质数p,然后将连续的p个ranwen翻过来
你希望让所有ranwen趴着。请计算完成这一任务所需的最少操作次数。
【输入格式】
从文件sexy.in中读入数据。
第一行n;
第二行n个整数xi。
【输出格式】
输出到文件sexy.out中。
一行一个整数。
【样例输入】
Sample #1
2
4 5
Sample #2
9
1 2 3 4 5 6 7 8 9
Sample #3
2
1 10000000
【样例输出】
Sample #1
2
Sample #2
3
Sample #3
4
【数据范围】
所有的数字都表示小于等于。
Xi<=1e7。
编号 n
1-2 2
3-4 5
5-7 15
8-10 1000
【提示】
样例一可以先选择5,并翻转1,2,3,4,5。然后选择3,并翻转1,2,3
这个题是最难想的。
首先需要转化一下,在1-1e7内根据条件建立差分数组,一个位置上的值为一的条件是这个位置上ranwen的状态与前一个位置上的不同。
然后可以得到类似01101100这样的数列.。接下来,我们考虑,当所有的(1-正无穷)全部为0时就完成任务了,而我们的操作是选择一段区间使ranwen反向,这会导致区间的左端点和右端+1的位置01翻转,而不影响其他位置。那么我们来看操作这样一段区间的代价。
1.区间长度为奇质数,代价为1.
2.区间长度为一个偶数,当其为2时用7-5,为4时用7-3,更大时用哥德巴赫猜想能用两个质数相加得到,代价为2.
3.区间长度为一个奇数,用哥德巴赫猜想,则代价为3.
因此我们就可以贪心得尽量先用方法1,然后方法2,然后方法3.
在方法一中,问题又可以转化为二分图匹配。因为大于2的质数都是奇数,因此可以把奇数和偶数分在两边两两连边的条件为差是质数,然后做最大匹配。这些匹配上的点代价为1.然后左边自身剩下的两两尽量配对,右边一样,代价为二,最后两边剩下的相互匹配,代价为3.

#include<stdio.h>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<bitset>
#include<vector>
#include<math.h>
using namespace std;
int n;
int x[1050];
int pri[10000000];
int lft[1050],rit[1050],link[1050],cnt,cntl,cntr;
int dif[10000005];
bool vis[10000005],use[1050];
vector<int>g[1050];
void ini()
{
    vis[1]=1;
    for(int i=2;i<10000005;i++)
    {
        if(vis[i])continue;
        pri[++cnt]=i;
        for(int j=2;i*j<10000005;j++)vis[i*j]=1;
    }
}
bool dfs(int x)
{
    for(int i=0;i<g[x].size();i++)
    {
        if(!use[g[x][i]])
        {
            use[g[x][i]]=1;
            if(!link[g[x][i]]||dfs(link[g[x][i]]))
            {
                link[g[x][i]]=x;
                return 1;
            }
        }
    }
    return 0;
}
int main(){
    ini();
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&x[i]);
        dif[x[i]]=1;
    }
    for(int i=1;i<10000005;i++)
    {
        if(dif[i]!=dif[i-1])
        {
            if(i&1)
            {
                lft[++cntl]=i;
            }
            else 
            {
                rit[++cntr]=i;
            }
        }
    }
    for(int i=1;i<=cntl;i++)
    {
        for(int j=1;j<=cntr;j++)
        {
            if(!vis[abs(lft[i]-rit[j])])
            {
                g[i].push_back(j);
            }
        }
    }
    int ans=0;
    for(int i=1;i<=cntl;i++)
    {
        memset(use,0,sizeof(use));
        ans+=dfs(i);
    }
    cout<<ans+(((cntl-ans)>>1)+((cntr-ans)>>1))*2+((cntl-ans)%2)*3<<endl;
}

T3
ranwen的服务器(network)
来自aqx的提示:树剖肯定TLE,考虑怎么用并查集做
【问题描述】
众所周知,ranwen建造出了强大的服务器网,规模十分庞大,有n个服务器,组成一个树形结构,1号点是总站,但是有时候可能会因为主机被回收,机房断电等事故造成服务器各种GG,现在有m个事件,可能为:1.查询x到根路径上第一个已经挂掉的服务器传输路径 2.x到y路径上的服务器传输路径全挂了。(不存在则输出0)
【输入格式】
从文件network.in中读入数据。
第一行n,m;
之后n-1行每行两个整数x,y;
之后m行每行1 x或者2 x y;
【输出格式】
输出到文件network.out中。
对于每个询问输出一行。
【样例输入】
5 4
1 2
1 3
2 4
2 5
1 2
2 2 3
1 3
1 4
【样例输出】
0
2
1
【数据范围】
所有的数字都表示小于等于。
编号 n m
1-3 1000 1000
4-5 200000 200000
6-7 600000 600000
8-10 1000000 1000000
这个题也比较毒,一般很容易想到树剖,然后线段树查找范围内位置最靠下的1的位置,考试拿了60.更大的点必然T,所以要换方法。
首先,当一条边坏掉后,之后的修改就对他没有意义了,因此一条边坏掉的时候可以通过记录他最先坏掉的时间来维护。我们可以用并查集记录每条边向上走第一条好边的编号,从而快速求出每条边坏掉的时间。接下来,可以考虑倒叙用并查集维护每个点向上沿着好边走达到的最高位置,那么当遇到询问时,直接find目标点的最高级fa就可以,遇到修改时,因为是倒叙,所以这里是把对应的点修好,那么在往上的过程中,遇到本次可以修好的边就再次把他指向他往上的第一个点,就算做修理好了。复杂度竟然只有 O(n) O ( n ∗ 玄 学 ) 。不过并查集的话,玄学也不会太大。

#include<stdio.h>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<bitset>
#include<vector>
#include<math.h>
using namespace std;
int n,m;
int fa[1000005],h[2000005],fr[2000005],to[2000005],num[2000005],cnt,tot;
int dep[1000005],dad[1000005],rd[1000005][2],tim[1000005],ans[1000005],f[1000005];
struct que{
    int cao,x,y;
};
que q[1000005];
void add(int x,int y,int id)
{
    fr[++cnt]=h[x];
    h[x]=cnt;
    to[cnt]=y;
    num[cnt]=id;
}
void ini()
{
    for(int i=0;i<1000005;i++)
        fa[i]=i;
}
int find(int x)
{
    return fa[x]==x?x:fa[x]=find(fa[x]);
}
void dfs(int now,int father)
{
    dep[now]=dep[father]+1;
    for(int i=h[now];i;i=fr[i])
    {
        if(to[i]!=father)
        {
            dad[to[i]]=now;
            f[to[i]]=num[i];
            dfs(to[i],now);
        }
    }
}
void del(int x,int y,int z)
{
    int fx=find(x);
    int fy=find(y);
    while(fx!=fy)
    {
        if(dep[fx]<dep[fy])
        {
            swap(fx,fy);
        }
        if(!tim[fx])
        {
            tim[fx]=z;
            fa[fx]=fa[dad[fx]];
        }
        fx=find(fx);
    }
}
void solve1(int x,int y,int z)
{
    while(x!=y)
    {
        if(dep[x]<dep[y])swap(x,y);
        if(tim[x]==z)
        {
            fa[x]=fa[dad[x]];
        }
        x=dad[x];
    }
}
int main(){
    ini();
    scanf("%d%d",&n,&m);
    for(int i=1,x,y;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        add(x,y,i);
        add(y,x,i);
        rd[i][0]=x;
        rd[i][1]=y;
    }
    dfs(1,0);
    for(int i=1;i<=m;i++)
    {
        scanf("%d",&q[i].cao);
        if(q[i].cao==1)
        {
            scanf("%d",&q[i].x);
        }
        else
        {
            scanf("%d%d",&q[i].x,&q[i].y);
            del(q[i].x,q[i].y,i);
        }
    }
    ini();
    for(int i=1,x,y;i<n;i++)
    {
        x=rd[i][0];
        y=rd[i][1];
        if(dep[x]<dep[y])
        {
            swap(x,y);
        }
        if(tim[x]==0)
        {
            fa[x]=y;
        }
    }
    for(int i=m;i>=1;i--)
    {
        if(q[i].cao==1)
        {
            ans[++tot]=f[find(q[i].x)];
        }
        else
        {
            solve1(q[i].x,q[i].y,i);
        }
    }
    for(int i=tot;i>=1;i--)
    {
        printf("%d\n",ans[i]);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值