2015沈阳赛区总结 与 简单题题解

原创 2015年10月27日 16:24:00

作为杭电三队,我和大三学长陈钦况,还有大一便进过WF的周贤杰组队。踏上了前往沈阳的行程。本来以平时训练的成绩,我们的期望是争金,最后却只打了个铜回来。着实是很意外,很遗憾。然而却有很多值得总结的经验和许多要吸取的教训,为我今后的比赛做准备。

比赛重现:

第20分钟,队友AC掉D题。

D题——

【题意】
共有T([1,500])组数据。
每组数据有1~n([2,20000])共n个正整数。
一开始对于已有数字集合,除了a,b(1<=a,b<=n && a≠b),其他数字都是不存在的。
对于每次成功的操作,我们是从已有数字集合之中,任选两个不同数x,y,得到z=x-y或者z=x+y,如果z是在[1,n]范围,而且z这个数字当前不存在,那我们就可以得到这个新数z,并把其放入已有数字集合之中。
Yuwgna先手,Iaka后手,谁无法操作谁就输了。问你最后的winner是谁。

【类型】
签到 博弈 gcd

【分析】
首先应该想的问题是,数轴的范围是[1,n],但是我们能选的数究竟有什么呢?
样例中的{3 1 3}{8 6 8}这样组数据,前者可以取遍1,2,3,后者却只能取得2,4,6,8,让我们很快想到实际能选取的数字范围要由gcd判定。
我们先令g=gcd(x,y),那显然我们能取的数,必然是g的倍数。
于是求出,接下来能取的数的个数num=n/g-2
所以答案就是——

【时间复杂度&&优化】
O(Tlogn)

【trick&&吐槽】
这道题之所以给了这么多样例,就是为了让这题的结论更容易观察,让这题更算得上是签到题。

【数据】
input
16
2 1 2
3 1 3
67 1 2
100 1 2
8 6 8
9 6 8
10 6 8
11 6 8
12 6 8
13 6 8
14 6 8
15 6 8
16 6 8
1314 6 8
1994 1 13
1994 7 12

output略

【代码】

int gcd(int x,int y)
{
    return y==0?x:gcd(y,x%y);
}
int main()
{
    scanf("%d",&casenum);
    for(casei=1;casei<=casenum;casei++)
    {
        int n,a,b;
        scanf("%d%d%d",&n,&a,&b);   
        printf("Case #%d: %s\n",casei,(n/gcd(a,b))&1?"Yuwgna":"Iaka");
    }
    return 0;
}

===========================================================
第50分钟,我AC掉M题。

M题——

【题意】
两个人A,B想要见面。
有n([2,1e5])个点,A初始在1点,B初始在n点。
有m个集合关系,第i个集合有Si个点,这些点两两之间移动所花费的时间都为dis([1,1e9]),有∑Si<=1e6。
让你输出A和B在哪些点碰面,使得他们能在最早时间相遇。
输出这个最早相遇时间以及所有满足的点。

【类型】
最短路

【分析】
这题很显然是一个最短路模型。然而如果暴力建边,边数可达1e12条,爆炸。
问题是怎么处理集合关系。比较好想,我读完题的瞬间就想到了做法——拆点。
对每个集合构造两个点,入点和出点,之间连一条边权为dis的边,
把集合内的每个点向这个集合的入点连边,边权为0,
把集合的出点向这个集合内的每个点连边,边权为0。
这样就实现了,对于每个集合,利用2Si+1条边,改变其内任意两个点的边权都为dis。
然后分别以1和n为起点,跑最短路,然后扫描一下即可。

【时间复杂度&&优化】
O(mlogm)

【trick&&吐槽】
点数N=n+2*最大集合数=1e5+2e6
边数M=3*最大集合数=3e6

【数据】
input
2
5 4
1 3 1 2 3
2 2 3 4
10 2 1 5
3 3 3 4 5
3 1
1 2 1 2

output
Case #1: 3
3 4
Case #2: Evil John

【代码】

const int N=1e5+2e6+10,M=3e6+10,Z=1e9+7,ms63=1061109567;
int casenum,casei;
int id;
int first[N],w[M],c[M],nxt[M];
LL f[N],F[N];
bool e[N];
struct node
{
    int x;LL v;
    node(){}
    node(int x_,LL v_){x=x_;v=v_;}
    bool operator < (const node& b)const{return v>b.v;}
};
void ins(int x,int y,int z)
{
    id++;
    w[id]=y;
    c[id]=z;
    nxt[id]=first[x];
    first[x]=id;
}
priority_queue<node>q;
void inq(int x,LL dis)
{
    if(dis>=f[x])return;
    f[x]=dis;
    q.push(node(x,dis));
}
void dijkstra(int st)
{
    MS(f,63);
    MS(e,0);
    inq(st,0);
    q.push(node(st,0));
    while(!q.empty())
    {
        int x=q.top().x;q.pop();
        if(e[x])continue;e[x]=1;
        for(int z=first[x];z;z=nxt[z])inq(w[z],f[x]+c[z]);
    }
}
int main()
{
    scanf("%d",&casenum);
    for(casei=1;casei<=casenum;casei++)
    {
        MS(first,0);id=1;
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)
        {
            int dis,g,x;
            scanf("%d%d",&dis,&g);
            int in=n+i;
            int out=n+m+i;
            ins(in,out,dis);
            while(g--)
            {
                scanf("%d",&x);
                ins(x,in,0);
                ins(out,x,0);
            }
        }
        dijkstra(1);
        MC(F,f);
        dijkstra(n);
        LL ans=1e18;
        int ed;
        for(int i=1;i<=n;i++)
        {
            gmax(F[i],f[i]);
            if(F[i]<=ans)
            {
                ans=F[i];
                ed=i;
            }
        }
        if(ans==1e18)printf("Case #%d: Evil John\n",casei);
        else
        {
            printf("Case #%d: %lld\n",casei,ans);
            for(int i=1;i<ed;i++)if(F[i]==ans)printf("%d ",i);
            printf("%d\n",ed);
        }
    }
    return 0;
}

===========================================================
然后队友卡在B题半个小时,他们开始让我一起思考B题。
因为这场比赛总共的题数高达13个,高级数据结构和算法又是他们俩写,他们想了很久都没想出做法,而当时AC的人数又很多。于是我想也许采取一定方向的暴力做法就能AC,便开始敲

不过暴力做法有两种。
首先,最直接最暴力的做法是——

int bf()
{
    for(int i=n;i>=1;i--)
    {
        for(int j=1;j<i;j++)
        {
            if(!strstr(s[j],s[i]))return i;
        }
    }
    return -1;
}

这个时间复杂度是O(Tnnlen),可达50*500*500*2000=250e8,即250亿,爆炸。
但是转念一想,这种数据所对应的输出可达50*500*2000=5e7,即50MB,是不可能的,读入就爆炸了。于是,如果常数小,这样做是可以AC的。
而事实上,对于随机数据,这种暴力其实比下面的暴力,效率更高,更容易AC。

然后,另外一种暴力,是我想要针对特定构造数据(其实是针对了自己TwT)所写。
以每个串为子串筛后面的所有串:
如果它不是后串的子串,那么break掉。答案不会比当前后串的编号小。
如果它是后串的子串,那么我们可以把后串删掉。
(就是这里想错了。我们应当删掉的串不是后串,而是这个串——如果顺着这里想下去,改变for循环的顺序,for j=i+1 to n,也许就能很快做出来了。唉,还是自己思考的时间太少了,思维太不灵活了。)

正确的做法是什么呢?
参照下文代码,先升序枚举i,再升序枚举j。
如果j是i之后的第一个满足要求的串,使得s[i]是s[j]的子串,那么s[i]对于后串的意义s[j]都能起到。使s[i] break即可。
否则s[i]不是s[j]的子串,那么s[j]就是满足要求的串,以后就不用再比较”某个串是不是s[j]的子串”了。
这个时间复杂度是什么呢?
对于每次(s[i]是不是s[j])的子串:如果是,break掉,s[i]不再匹配;如果不是,s[j]被确定满足要求,s[j]不再匹配。所以每次比较都会有一个串失去后序匹配功能。
于是时间复杂度是O(T(n^2+nlen)),最大不过50*500*2000=5e7,就是一个完全可以AC的时间复杂度了。

【时间复杂度&&优化】
O(Tnnlen)->O(T(n^2+nlen))

【trick&&吐槽】
1,题目:做题要看题目名称暗示。B题题目bazinga是”逗你玩”的意思,然后我们真的被这题捉弄了。
2,读题:不要太依赖队友的读题,做一道题之前一定要自己读一遍,形成一个独立、系统的认知。很多时候,水题做不出来,都是队友开题,然后甩给我,我的思维附带了他们之前的错误思路,也就很难走出去。
3,策略:不要让队友卡题,尤其这种傻X题,不如让自己来卡。不要对自己生疏的算法有所恐惧,要挑起队伍的旗帜。
4,思维:思维要灵活。这题其实关键就是两个for循环的顺序,只要我都试着思考下,尝试下,很快就能做出来的。
5,我一开始的暴力做法,是想要剪枝的,但是在细节地方没有想清楚,可是如果抓住问题,思维严谨有序地想下去,也会很快出解。思考时间(而不是编码时间)应该是解决问题的大头,想清楚细节再做题是非常重要的。
6,strstr(母串,子串)返回的是NULL或者母串的匹配首位点指针。这个实际比KMP都要快。用这个的话这道题也不会卡住了。

总结——所以对于水题:
1,我来做
2,重新系统读遍题
3,灵活地做思维转化

【数据】
input
4
5
ab
abc
zabc
abcd
zabcd
4
you
lovinyou
aboutlovinyou
allaboutlovinyou
5
de
def
abcd
abcde
abcdef
3
a
ba
ccc

output
Case #1: 4
Case #2: -1
Case #3: 4
Case #4: 3

【代码】

char s[505][2020];
bool e[505];
int solve()
{
    MS(e,1);int ans=-1;
    for(int i=1;i<n;i++)//枚举子串
    {
        for(int j=i+1;j<=n;j++)if(e[j])//枚举还不一定是满足串的母串
        {
            //如果j是i之后的第一个满足要求的串,使得s[i]是s[j]的子串,那么s[i]对于后串的意义s[j]都能起到。使s[i] break即可。
            if(strstr(s[j],s[i]))break;
            //否则s[i]不是s[j]的子串,那么s[j]就是满足要求的串,以后就不用再比较了。
            else {e[j]=0;gmax(ans,j);}
        }
    }
    return ans;
}
int main()
{
    scanf("%d",&casenum);
    for(casei=1;casei<=casenum;casei++)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%s",s[i]);
        printf("Case #%d: %d\n",casei,solve());
    }
    return 0;
}

队友卡了3个小时B题。他们尝试做F和I都失败了。(其实F很接近了>_<)。
这个时间我都是在做G题的。
虽然G题在封榜之前只有2个队AC,但是鉴于尝试的队伍有一些,而且这题题目很长,有模拟题的模样,于是我选择了它。
事实上,赛后,AHdoc也说这场比赛最水的4道题便是D,B,M,G,然而G实在是因为题意太坑=w=,最后我们还是以史上最差战绩3题收场。

===========================================================
G题——
【trick&&吐槽】
1,这题让我懂得了:对于很多坑B题,要一句句全部读懂才能做。
这些作者多多少少有些心理变态,重点积极分散,使得题目像是阅读理解一样来考你。

2,有的题目,对于重要信息,题目上下文会多次重复提及。
然而也有题目,上下文看似相同的两处信息,其实要有不一样的理解。

3,噢。在我整理这道题题解的时候,从出题人处得到了中文题。发现这题认真理解题意的话,就是个翻译错的题。
需要脑补才能AC,脑补才能AC,脑补才能AC……你要多读,多猜,多想,自行发现哪里可能有歧义。
翻译是把reach和touch搞错了。而且原文消除歧义的一大段文字,在翻译中一字未现。
所以才导致了各种无限wa的情况,甚至包括做CodeChef擅长阅读理解的朝鲜人12wa未AC。

中文原文:
这里写图片描述
英文翻译:
It is also assumed that if A and S touch the buoy in the same time,the point will be given to A and A could also fight with S at the buoy.
We assume that in such scenario,the dogfighting must happen after the buoy is touched by A or S.

【题意】
一个正方形球场。每个角都有一个浮标。按照顺时针方向,依次编号为#1,#2,#3,#4。
两个运动员开始都在1号球杆,他们要先后触碰2号,3号,4号,1号球杆,绕圈一周。

游戏规则:
1,对于任何一个浮标,如果你触碰的时间比对手早,那么你可以得到1分。但是触碰浮标的顺序必须严格是#2,#3,#4,#1。
2,如果你和对手同时到达了一个位置。你们可以打一场,赢的人得一分。但是为了保证比赛的平衡,2个运动员不允许在#2被触碰之前打架。

我们的速度是v1,对手的速度是v2,满足:0< v1<=v2<=2000。
对手会严格按照矩形的边,即按照1->2->3->4->1的顺序移动。
我们一定能打过对手,于是我们决定触发有且仅有一次战斗。这会使得我们多得到1分,并且使得对手立刻眩晕T秒。
如果2个人同时到触碰一个点,这个点的得分会给与我们,且我们还可以发动与对手的战斗。
如果2个人恰好在一个点相遇,如果对手碰过上一个浮标,那这个点的得分会给对手。
让你输出是否我们有可能得分比对手高。可能便输出Yes,不可能则输出No。

【类型】
阅读理解坑题TwT 解方程or二分

【分析】
这题的逻辑可以很清楚。
1,如果速度相同,那么我们必胜。
2,如果速度不同,我们有意义的阻截位置,要不是在2->3,要不是在3->4。
因为我们最终肯定要沿着#2->#3->#4->#1的顺序走,所以肯定满足——触碰位置越早越好。

所以存在三个判定点,
1,速度相同,我方必胜。
2,[#2 ~ #3)之间相遇,看我方能否先到达#4。
3,[#3 ~ #4)之间相遇,看我方能否先到达#1。
4,[#4 ~ #1)相遇,我方必败。

【时间复杂度&&优化】
O(T*二分)

【数据】
Input
2
1 10 13
100 10 13

Output
Case #1: No
Case #2: Yes

specail(恰好相遇于#4):
2000 100 300
我们和对方都同时在第3s到达,我方必败。

special(恰好相遇于#4前):
2000 100.01 300
眩晕时间很长,我方胜。

special[1+]
12.25 100.01 300
恰好胜

special[2+]
12.24 100.01 300
恰好败

【代码】

double T,v1,v2,T1,T2;
double K(double x){return x*x;}
void YES(){printf("Case #%d: Yes\n",casei);}
void NO(){printf("Case #%d: No\n",casei);}
void solve()
{   
    if(v1==v2)YES();
    else if(v1*v1*2>v2*v2)//最早能在#3之前相遇,二分相遇点的横坐标
    {
        double l=0;
        double r=300;
        for(int tim=1;tim<=100;tim++)
        {
            double m=(l+r)/2;
            double l1=sqrt(K(300)+K(m));
            double t1=l1/v1;
            double l2=300+m;
            double t2=l2/v2;
            t1>t2?l=m:r=m;//如果来不及,那么相遇点横坐标变大,否则横坐标变小。
        }
        //如果我们想要获胜,必须至少要触碰#3和#4。
        double t1=sqrt(K(300)+K(l))/v1+l/v1+2*T1;//我们触碰#4的时间
        double t2=3*T2+T;//对手触碰#4的时间
        t1<=t2?YES():NO();
    }
    else if(v1*3>v2)//最早能在#4之前相遇,二分相遇点的纵坐标
    {
        double l=0;
        double r=300;
        for(int tim=1;tim<=100;tim++)
        {
            double m=(l+r)/2;
            double l1=sqrt(K(300)+K(m));
            double t1=l1/v1;
            double l2=900-m;
            double t2=l2/v2;
            t1>t2?r=m:l=m;//如果来不及,那么相遇点纵坐标变小,否则纵坐标变大。
        }
        //如果我们想要获胜,必须至少要触碰#4和#1
        double xx=sqrt(K(300)+K(l))/v1;
        double yy=sqrt(K(300)+K(300-l))/v1;
        double t1=sqrt(K(300)+K(l))/v1+sqrt(K(300)+K(300-l))/v1+3*T1;//我们触碰#1的时间
        double t2=4*T2+T;//对手触碰#1的时间
        t1<=t2?YES():NO();
    }
    else NO();
}
int main()
{
    scanf("%d",&casenum);
    for(casei=1;casei<=casenum;casei++)
    {
        scanf("%lf%lf%lf",&T,&v1,&v2);
        T1=300/v1;
        T2=300/v2;
        solve();
    }
    return 0;
}

G题因为题意问题,加上我太蠢没有学会潜在坑点分析+脑补。
加上B题我没有担当起责任,没有解放队友,导致最终打了个铜。

主要原因还是自己太弱。
争取在上海的时候拿个金,至少正常水平发挥拿到ECfinal参赛权。
队友一个进过WF没了干劲,另外一个想要找工作,也没了热情。虽然他们水平很厉害,但希望在明年,能够不再和”马上打比赛的时候嘴里却喊着退役,什么结果都无所谓的人”组队,消极的队友,会带给你一种无力感,真的很累很累。其实还不如单挑,虽然名次会变低,但是至少充满斗志和热情。

加油!冲击明年的WF。一切都会变好!

版权声明:题解中哪里写错请一定要指出来QwQ 转载还请注明下出处哦,谢谢^_^

HDU 3790 最短路径问题【最短路 dijkstra 双权值】

最短路径问题 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total S...
  • wyjwyl
  • wyjwyl
  • 2015年08月17日 22:01
  • 278

如何使用bmp位图制作字符画

通过分析bmp文件制作字符画

【HDU5512 2015沈阳赛区D】【签到题 gcd博弈】Pagodas 取数x+y或x-y

Pagodas Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Su...

HDU 5510 2015ACM-ICPC沈阳赛区现场赛B题

BazingaTime Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others) Total Su...

HDU 5512 2015ACM-ICPC沈阳赛区现场赛D题

PagodasTime Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others) Total Su...

HDU-5512-Pagodas【2015沈阳赛区】

HDU-5512-Pagodas

hdu 5512 (Pagodas) 2015 ACM/ICPC 亚洲区沈阳赛区 (题目编号1004)

Problem Description n pagodas were standing erect in Hong Jue Si between the Niushou Mountain and th...
  • kavu1
  • kavu1
  • 2015年10月31日 21:32
  • 1637

【HDU5510 2015沈阳赛区B】【KMP or strstr for循环剪枝】Bazinga 循环处思维灵活转化 时间复杂度均摊思想

Bazinga Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Su...

【HDU5521 2015沈阳赛区M】【拆点最短路 dijkstra+heap】Meeting 集合内距离相同

Meeting Time Limit: 12000/6000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others) Total...

hdu5458 2015沈阳赛区网络赛 树链剖分+并查集

题意:给你个图,然后给你m条边,然后有两种操作,第一种是1,删除两个点之间的一条边,第二种是2,问两个点之间的稳定性(稳定性的定义是删除一条边后这两个点就会不连通,这样的边有多少条它的稳定性就是多少,...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:2015沈阳赛区总结 与 简单题题解
举报原因:
原因补充:

(最多只允许输入30个字)