NOIP2015解题报告

来源:互联网 发布:php留言板视频教程 编辑:程序博客网 时间:2024/05/01 00:43

Day1.
当时的zxn很弱,弱到连dfs都调不明白就开始去NOIP。
现在他会了dfs,二分答案,求LCA,bfs,拓扑排序。
所以他回去填NOIP2015的题解坑。
T1.我现在依然不知道除了这种尾递归式的写法之外还有啥别的写法……
伪代码:

void dfs(int i,int j,int x){    if(满足条件)w[i][j] = x;    dfs(i',j',x + 1);}

咳我好像现在明白了……
大概一个循环确实能搞出来……
T2.
求最小环,当时zxn心里确实也是这么想的。
由于他太弱了不会dfs,所以他并不知道怎么写。
题解:
dfs一遍,记一下时间戳。

void dfs(int x){    vis[x] = 1;    tid[x] = ++ tim;    RepG(i,x)        if(!vis[v])dfs(v);        else ans = min(ans,tid[x] - tid[v] + 1);    //大概是这样的吧}

然后后来仔细一想,嗯我还是bfs吧。
次奥……?T掉了QAQ
嗯我们还是冷静一下,发现……
如果是bfs,需要拓扑排序,删掉没有用的那些点,只剩下环即可。

#include<algorithm>#include<cmath>#include<cstdio>#include<cstring>#define Rep(i,n) for(int i = 1; i <= n ; i ++)#define RepG(i,x) for(int i = head[x] ;~ i ; i = edge[i].next)#define Rep_d(i,n) for(int i = n ; i > 0 ; i --)#define Rep_0(i,n) for(int i = 0 ; i < n ; i ++)#define RD(i,x,n) for(int i = x; i <= n ; i ++)#define CLR(a,b) memset(a,b,sizeof(a))#define v edge[i].tousing namespace std;int read(){    char ch = getchar();    while(ch < '0' || ch > '9')ch = getchar ();    int x = 0;    while(ch >= '0' && ch <= '9')x = 10 * x + ch - '0',ch = getchar ();    return x;}int to[200005];bool vis[200005],vis_now[200005];int q[200005],tid[200005],tim = 0,ans = 1 << 30,end[200005];int main(){    int n = read();    Rep(i,n)    {        int a = read();        to[i] = a;          end[to[i]] ++;    }    int h = 0,t = -1;       Rep(i,n)        if(!end[i])vis[q[++ t] = i] = 1;    while(h <= t){        int x = q[h ++];        end[to[x]] --;        if(!end[to[x]])vis[q[++ t] = to[x]] = 1;    }    Rep(i,n){        if(!vis[i]){            h = 0,t = -1;            vis[i] = 1;            q[++ t] = i;            tim = 0;            tid[i] = 0;            while(h <= t){                int x = q[h ++];                if(!vis_now[to[x]])vis_now[q[++ t] = to[x]] = 1,tid[to[x]] = ++ tim,vis[to[x]] = 1;                else ans = min(ans,tim - tid[to[x]] + 1),h = t + 1;            }        }    }    printf("%d\n",ans);    return 0;}

QAQ当时明明知道标算然而就是写不出来?
T3.
斗地主。
张地主出的一道水题。张地主亲口说道:“NOIP的一道水题。”
张地主这次CTSC还出了一道提交答案题。
“我们可以发现第8个点是个网格图。”
“第九个点是除了前几个调换了下顺序之外的网格图。”
“第十个点是挖掉了一些点的网格图。”
whx:“我要吐槽!怎么检验它是网格图呢?”
“我的暴力spfa怎么跑的这么慢呢……?”
TAT
题解:
暴力搜索即可。
我们考虑对于当前的手牌,先枚举顺子应该会优一点,因为这样方便加最优性剪枝。
我们考虑:
if(ans <= depth )return;
加这个剪枝就过了。

#include<algorithm>#include<cmath>#include<cstdio>#include<cstring>#define Rep(i,n) for(int i = 1; i <= n ; i ++)#define RepG(i,x) for(int i = head[x] ;~ i ; i = edge[i].next)#define Rep_d(i,n) for(int i = n ; i > 0 ; i --)#define Rep_0(i,n) for(int i = 0 ; i < n ; i ++)#define RD(i,x,n) for(int i = x; i <= n ; i ++)#define CLR(a,b) memset(a,b,sizeof(a))#define v edge[i].tousing namespace std;int s[20];int read(){    char ch = getchar();    while(ch < '0' || ch > '9')ch = getchar ();    int x = 0;    while(ch >= '0' && ch <= '9')x = 10 * x + ch - '0',ch = getchar ();    return x;}int Cases,n,ans;void dfs(int now){    if(now >= ans)return;    int a = 0,b = 0,c = 0;    Rep_0(i,14)if(s[i] == 1)a ++;    Rep_0(i,13)if(s[i] == 2)b ++;    Rep_0(i,13)        if(s[i] == 4){            c ++;            if(a >= 2)a -= 2;            else if(b >= 2)b -= 2;            else if(b >= 1)b --;        }    Rep_0(i,13)        if(s[i] == 3){            c ++;            if(a >= 1)a --;            else if(b >= 1)b --;        }    ans = min(ans,a + b + c + now);    Rep_0(i,8){        int j;        for(j = i ; j <= 11 ; j ++){            s[j] --;            if(s[j] < 0)break;            if(j - i >= 4)dfs(now + 1);        }        if(j == 12)j --;        while(j >= i)s[j --] ++;    }    Rep_0(i,10){        int j;        for(j = i; j <= 11; j ++){            s[j] -= 2;            if(s[j] < 0)break;            if(j - i >= 2)dfs(now + 1);        }        if(j == 12)j --;        while(j >= i)s[j --] += 2;    }    Rep_0(i,11){        int j;        for(j = i; j <= 11; j ++){            s[j] -= 3;            if(s[j] < 0)break;            if(j - i > 0)dfs(now + 1);        }        if(j == 12)j --;        while(j >= i)s[j --] += 3;    }}int main(){    Cases = read(),n = read();    while(Cases --){        CLR(s,0);        ans = 10005;        Rep(i,n){            int c = read(),col = read();            if(c < 3 && c)s[10 + c] ++;            else if(c >= 3)s[c - 3] ++ ;            else s[13] ++;        }        dfs(0);        if(s[13] == 2)ans ++;        printf("%d\n",ans);    }    return 0;}

当时NOIP的时候,zxn表示自己:
“这个可能是某种神奇的搜索,估计写不出来。”
mdzz。
Day2.
zxn:”day1好像挺水的,虽然我不会做,但是算法还是都看出来的。day2应该很友善.”
T1.
跳石头。
zxn:”什么叫二分答案???”
题解:我们对最终那个” 最小的距离最大” 进行二分答案。
也就是说,我们考虑二分那个值,判断是否可行。
现在问题在于怎么O(n)判断可行。
考虑现在有两块石头i和j(i < j )它们连在一起,现在它们的距离小于二分的答案,我们现在想一下该搬哪块……
嗯……
显然是搬走j更优。
我们现在需要考虑的仅仅是这个答案是否可行,所以我们现在面临的条件就是是否能让它花费的石头最少。
考虑这样:
这里写图片描述
i和j的距离小于二分的距离,并且j和j + 1的距离也小于二分距离。
我们肯定是要继续往后走的。
我们删去i的话以后影响的距离并不受这个i石头的控制,但是我们删去j了之后,不仅消除了前面的i - > j的不合法,而且还有可能让后面的变得合法。
所以删去石头j,即当前扫到的这个石头更优。

#include<algorithm>#include<cmath>#include<cstdio>#include<cstring>#define Rep(i,n) for(int i = 1; i <= n ; i ++)#define Rep_0(i,n) for(int i = 0 ; i < n ; i ++)#define RD(i,x,n) for(int i = x; i <= n ; i ++)#define CLR(a,b) memset(a,b,sizeof(a))#define v edge[i].tousing namespace std;int read(){    char ch = getchar();    while(ch < '0' || ch > '9')ch = getchar ();    int x = 0;    while(ch >= '0' && ch <= '9')x = 10 * x + ch - '0',ch = getchar ();    return x;}int n,m,L;int dis[50005];bool check(int x){    int p = 0 ,k = 0;    Rep(i,n + 1){        if(dis[i] - dis[k] < x)p ++;        else k = i;        if(p > m)return 0;    }    return 1;}int Bin_ans(int l,int r){    if(l == r)return l;     int mid = l + r + 1 >> 1;    if(check(mid))return Bin_ans(mid,r);    return Bin_ans(l,mid - 1);}int main(){    L = read(),n = read(),m = read();    Rep(i,n)        dis[i] = read();    dis[0] = 0;    dis[n + 1] = L;    printf("%d\n",Bin_ans(1,L));    return 0;}

T2.
zxn在一个月前不可置信地问fsf:”这怎么可能是一道特别简单的DP?”
现在他看到这道题:”哦我自己真是智障。”
设f[i][j][k]表示A串到i,B串到j,一共搞了k个串连起来的方案数。

(a[i]==b[j] and a[i1]!=b[j1])f[i][j][k]=pf[p][j1][k1]

(a[i]==b[j] and a[i1]==b[j1])f[i][j][k]=pf[p][j1][k1]+f[i1][j1][k]

前缀和 + 滚动数组优化。
这就没了啊QAQ
T3.
zxn原来一直读错题了。
他读成了使得所有的花费总和最小。
题解:使得最大的花费最小,显然二分答案。
问题是怎么check。
我们可以利用它的LCA。
我们考虑这两个点(u,v),如果dis(u,v) >ans,那是要担责任的。
我们把u - > v的路径都标一遍,表示它们用过一遍,并且我们把距离答案最大的差记为lim。
假设我们有p条边要担责任。
当有一条边a它的边权 >= lim,且有use[a] == p,那么我们就可以删掉它了。
总的复杂度我一开始以为这是个暴力,所以一直觉得自己不会做。
现在想想每次复杂度是可以优化成O(n)的啊QAQ
考虑我们每次只标一下那两个节点和它们的LCA。
我们直接对它们到LCA的路径都加一下就好了,但是如果每一个都单独加的话显然是不优的。
我们考虑其实这个玩意可以用dfs序优化一发,就是我们每次找叶子节点进行往上的递推。
这样就是O(n)的辣QAQ
总复杂度是O(nlog2n)
所以zxn还是太弱了QAQ

0 0
原创粉丝点击