2017.10.25考试总结

来源:互联网 发布:美发店收银软件 编辑:程序博客网 时间:2024/05/18 07:58

总结

其实这次考试的题目并不难,但是却考得比较崩。总结下原因有这么些:

  • 思维不够缜密。这也是最重要的,想到非正解思路之后,还没有验证它的正确性,就开始打代码,导致浪费了大把的时间。
  • 考试的策略问题。做题顺序的不当,导致了我第二题没拿到该拿的分,尤其需要注意调整,而且打对拍的也占用了较多的时间,三个半小时还是有点不太够用啊。
  • 刷题不够,效率较低。我做思维题做的不算多,各个算法、数据结构的各个类型的题目也没有全部覆盖到,正因为刷题的缺少,导致解题思维没有成型,而且效率也比较低,错误比较多。

梦幻布丁(HNOI2009)

Problem Description

详细请戳链接!
偷个懒应该没人会发现吧

Thoughts

第一眼就是线段树,这不明摆着嘛,利用最长连续零的套路,维护左右区间的颜色状态(杂色/Ci色),还有区间最左边最右边的颜色,顺便计数即可。然而,这种轻轻松松就AC的想法还是too naive了。但因为n,m<=200000,建树,加更新经常需要递归至最下层,效率比线段树低了很多,而且这题的常数比较大。
后来打完了,回来看这道题,发现可以优化,但是时间不够,开始打对拍。
我想的优化是,因为修改和查询都是对于全区间而言,那么颜色种类数k经过修改只可能变得更少,因为同种颜色必定会同时修改,不可能分为两种或多种颜色。而且既然线段树有较大的可能性要递归到最底层,那么就可以用zkw线段树优化,直接采用链式前向星的思想,修改特定颜色的时候,直接可以查询,并且把修改当做单点修改。
然后蒟蒻就想不到有什么比较好的优化方法了……然后在下考前几分钟草率地打了个对拍,拍了几百组就草率地交了。
期望得分:70 实际得分:50
考后,打了优化的思想,发现也是50分,数据一点梯度都没有啊喂

Solution

事实上,我从第一眼就被误导了,打线段树真个错误的决定。正解要用链表优化和启发式合并。
也就是,修改的时候,并不实际去进行修改,而是做标记,用nc[i]数组表示第i个现在是什么颜色,在读入的时候,就先处理出ans,修改时维护即可。在合并时,将元素较少的颜色合并到元素较多的颜色上。和题目中要求的反了?没关系,标记一下就好了。

Code

#include <iostream>#include <cstring>#include <cstdio>using namespace std;template <typename Tp> inline void read(Tp &x){    x=0;    char ch=getchar();    while(ch<'0'||ch>'9') ch=getchar();    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();}const int size=1000010;int n,m,ans,c[size],nc[size],head[size],tail[size],nxt[size],cnt[size];void merge(int a,int b){    if(!cnt[a])      return ;    cnt[b]+=cnt[a];    for(int i=head[a];~i;i=nxt[i])    {        if(c[i-1]==b)          ans--;        if(c[i+1]==b)          ans--;    }    for(int i=head[a];~i;i=nxt[i])      c[i]=b;    nxt[tail[b]]=head[a];//接起来    tail[b]=tail[a];    head[a]=tail[a]=-1;//将a颜色清空    cnt[a]=0;}int main(){    freopen("pudding.in","r",stdin);    freopen("pudding.out","w",stdout);    int k,u,v;    read(n),read(m);    memset(head,-1,sizeof(head));    memset(tail,-1,sizeof(tail));    for(int i=1;i<=n;i++)    {        read(c[i]);        nc[c[i]]=c[i];        if(c[i]!=c[i-1])          ans++;        if(tail[c[i]]==-1)          tail[c[i]]=i;        cnt[c[i]]++;        nxt[i]=head[c[i]];        head[c[i]]=i;    }    while(m--)    {        read(k);        if(k==2)          printf("%d\n",ans);        else        {            read(u),read(v);            if(u==v)              continue;            if(cnt[nc[u]]>cnt[nc[v]])              swap(nc[u],nc[v]);            merge(nc[u],nc[v]);        }    }    return 0;}

越狱(HNOI2008)

Problem Description

详细请戳链接!
再偷个懒应该还是没人会发现吧

Thoughts

考试的时候想复杂了,看了很久,觉得有点像组合数学+容斥原理,然后就需要知道连续两次的,减去连续三次,加上连续四次……但是这个常数不好推啊,我暴力+手推,推了两次公式,均被自己推翻。当时内心是崩溃的,而且心态又不是很好,因为先做了第三题(欲知有多惨,见下)……所以推了好久,没推出来,就去打对拍了,没救了,放弃治疗qwq
期望得分:0 实际得分:(已和谐)

Solution

事实上,第二题是最水的……看来只有我没有推出来,被各位神犇暴踩QAQ
考虑补集,首先所有方案是mn,如若要没有任意两个相邻方块颜色相同,显然我们有m(m1)n1,那么答案就是两者的差了,注意开long long,打个快速幂再%一下。

Code

#include <iostream>#include <cstdio>using namespace std;typedef long long ll;const ll mod=100003;ll n,m;ll power(ll a,ll b){    ll res=1;    while(b)    {        if(b&1)          res=res*a%mod;        a=a*a%mod;        b>>=1;    }    return res;}int main(){    freopen("prison.in","r",stdin);    freopen("prison.out","w",stdout);    scanf("%lld%lld",&m,&n);    printf("%lld\n",(power(m,n)+mod-(m*power(m-1,n-1)%mod))%mod);    return 0;}

Problem

Problem Description

没找到原题,偷不了懒了……

Description

给你一张含有 n 个点 m 条边的联通无向图,记录 1 号点到每个点的最短路长度,询问
去掉与 i 号相邻的所有边后,1 号点到多少个点的最短路长度改变,若不连通则也视为改
变。

Input

第一行两个正整数 n,m,
接下来 m 行,每行三个正整数数 i,j,k,表示一条边

Output

N 行,第 i 行表示去掉与 i 号相邻的所有边后,1 号点到多少个点的最短路长度改变。

Sample Input

2 1
1 2 1

Sample Output

1
1

Data Size

30% : n<=100,m<=300
100% :n<=5000,m<=20000,边权均为不超过 100 的正整数。

Thoughts

草率地看完题目后,我联想到了NOIP2013货车运输。刚开始的想法(错的!)是找一棵最小生成树,然后进行一次树形DP,找到每个节点的儿子节点并统计个数cnt,则删去该节点之后最短路径长发生改变的就是cnt。举了一个例子,挂了,发现当产生了有多条路径到x点均为最小权值时会挂,于是又做了各种麻烦的操作改……然而,打了一发对拍,惨烈地挂了。谁说最短路径的路径一定在最小生成树上的……
期望得分:0 实际得分:(已和谐)

Solution

正解需要先跑一边SPFA跑出单源最短路,然后再以每个点检查,找出作为最短路径的边,并重新建图,然后对于每一个删去的节点,bfs一下,查看能访问到多少节点,未访问到的就是改变的节点数。
这叫做建最短路图。貌似还是很有用的。
吐槽:重新建图超级麻烦,还要写两个不同的链式前向星,数组开到手抖

Code

#include <iostream>#include <cstring>#include <cstdio>#include <queue>using namespace std;template <typename Tp> inline void read(Tp &x){    x=0;    char ch=getchar();    while(ch<'0'||ch>'9') ch=getchar();    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();}const int maxn=5010,maxm=20010;int p,head[maxn],to[maxm<<1],w[maxm<<1],nxt[maxm<<1];int n,m,dis[maxn],que[maxn],p2,h2[maxn],t2[maxm<<1],n2[maxm<<1];bool inq[maxn],vis[maxn];queue<int> q;void insert(int u,int v,int tw){    to[++p]=v;    w[p]=tw;    nxt[p]=head[u];    head[u]=p;}void insert2(int u,int v){    t2[++p2]=v;    n2[p2]=h2[u];    h2[u]=p2;}void input(){    int u,v,tw;    read(n),read(m);    for(int i=1;i<=m;i++)    {        read(u),read(v),read(tw);        insert(u,v,tw);insert(v,u,tw);    }}void spfa(){    int now;    memset(dis,0x3f,sizeof(dis));    q.push(1);    dis[1]=0;inq[1]=true;    while(!q.empty())    {        now=q.front();        q.pop();        inq[now]=false;        for(int i=head[now];i;i=nxt[i])          if(dis[to[i]]>dis[now]+w[i])          {            int v=to[i];            dis[v]=dis[now]+w[i];            if(!inq[v])              inq[v]=true,q.push(v);          }    }    for(int i=1;i<=n;i++)    {        memset(vis,0,sizeof(vis));        for(int j=head[i];j;j=nxt[j])        {            int v=to[j];            if(!vis[v]&&dis[v]==dis[i]+w[j])              vis[v]=true,insert2(i,v);        }    }}void work(int k){    int h=1,t=1,x,ans=0;    memset(vis,0,sizeof(vis));    vis[1]=1;que[1]=1;    while(h<=t)    {        x=que[h++];        for(int i=h2[x];i;i=n2[i])          if(!vis[t2[i]]&&t2[i]!=k)            vis[t2[i]]=true,que[++t]=t2[i];    }    for(int i=1;i<=n;i++)      if(!vis[i])        ans++;    printf("%d\n",ans);}int main(){    freopen("problem.in","r",stdin);    freopen("problem.out","w",stdout);    input();    spfa();    printf("%d\n",n-1);    for(int i=2;i<=n;i++)      work(i);    return 0;}
原创粉丝点击