关于8 1的题解

1354: To The Beginning

时间限制: 1 Sec   内存限制: 128 MB   Special Judge
麻婆一股劲的追切丝,追到一个大森林里。切丝看着前面有一块大平地,可以当成是一个n*n的矩阵。其中只有n个面积为1*1的子矩阵上面有树。眼看着麻婆就要赶来了,切丝必须要很快地移动这些树(好强壮!),使得每行每列仅有一棵树,以挡住麻婆的视线。那么他最少要移动多少距离的树呢?他又怎样移动这些树呢?注:每次移动树你可以任选一棵树,但是树只能四方向移动,每移动到一个相邻的子矩阵距离为1。一次只能移动一棵,且不能移动到已经有树的子矩阵里面。
操作方案:”L”:(Y-1)  “R”:(Y+1)   “U”:(X-1)   “D”:(X+1)


看到切丝papa的瞬间我是慌乱的,所以我没有看到第一段的最后一句也是情有可原的。。。。本以为对于这些点要用二分图操作,万万没想到只需要贪心找到最优方案即可。当然,行列是不相关的,故此可以拆开。就行来说,越左边的自然要排在左边,故此只需要排序后贪心。(注意:贪心时请分别处理往左和往右来保证坑爹的顺序)列也一样。时间复杂度O(n^2) 。【输出的复杂度竟然更高。。。】

type niz = array[1..500] of integer;
 
var
   r, s : niz;
   n : integer;
   i, K : longint;
   rjesenje_tenk : array[1..2*500*500] of integer;
   rjesenje_smjer : array[1..2*500*500] of char;
 
procedure rasporedi( a : niz; n : integer; X, Y : char );
var
   index : niz;
   i, j, tmp : integer;
begin
   for i := 1 to n do
      index[i] := i;
 
 
   for i := 1 to n do
      for j := i+1 to n do
         if a[j] < a[i] then begin
            tmp := a[i]; a[i] := a[j]; a[j] := tmp;
            tmp := index[i]; index[i] := index[j]; index[j] := tmp;
         end;
 
   for i := 1 to n do
      while a[i] > i do begin
         a[i] := a[i] - 1;
         K := K + 1;
         rjesenje_tenk[K] := index[i];
         rjesenje_smjer[K] := X;
      end;
 
 
   for i := n downto 1 do
      while a[i] < i do begin
         a[i] := a[i] + 1;
         K := K + 1;
         rjesenje_tenk[K] := index[i];
         rjesenje_smjer[K] := Y;
      end;
end;
 
begin
 
   readln( n );
 
   for i := 1 to n do
      readln( r[i], s[i] );
 
   rasporedi( r, n, 'U', 'D' );
   rasporedi( s, n, 'L', 'R' );
 
   writeln( K );
   for i := 1 to K do
      writeln( rjesenje_tenk[i], ' ', rjesenje_smjer[i] );
end.
 







1355: Synchronicity

时间限制: 1 Sec   内存限制: 128 MB
大家知道什么叫做Synchronicity吗,其中文翻译为“共时性”,这是荣格对一些超自然的现象做出的解释。这些现象包括“说曹操曹操到”,“噩梦成真”等……说这么学术干嘛?Synchronicity不就是LMZ最爱的一首歌嘛!
说到共时性,“心有灵犀”也是一种类似的现象。话说法伊和由伊是双胞胎,他们俩由于在“时间之塔”里关了几百年,所以知识不够。他们现在在上数学课,然后在质因数。当然,他们俩很聪明,已经开始学习举一反三了。由伊随口说出一个n和p,法伊立刻就能说出最小质因子为p的第n个正整数(即恰好有n-1个最小质因子为p且比它小的数),其中p一定是质数。法伊之后又沉睡了,为了缓解由伊的寂寞,黑钢想代替法伊承担其任务。但是黑钢明显无法很快想到答案,于是就让你帮他想吧。


关于这道题,我必须沉默以对,因为它的正解就是暴力。然而,单单是暴力还是不够的,还需要强大的剪枝功力再加上欧拉筛法,还有一大坨特判请大家自己看着办。时间复杂度我也说不好。然而单纯暴力是有90分的。。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#define MAXN 1000000000
using namespace std;
bool flag[5000000];
int prime[5000000];
int main()
{
    int n,p,k=0;
    scanf("%d%d",&n,&p);
    if (p>32000) {if (n==1) printf("%d\n",p); else printf("0\n"); return 0;}
    long long ggg=(long long )p*n;
    if (ggg>MAXN) {printf("0\n"); return 0;}
    if (p==2) {printf("%d\n",n*2);return 0;}
    memset(flag,0,sizeof(0)); int tot=0;
    for (int i=2; i<=p-1; i++)
    {
        if (!flag[i]) prime[++tot]=i;
        for (int j=1; j<=tot; j++)
        {
            if (prime[j]*i>33000) break;
            flag[prime[j]*i]=1;
            if (i%prime[j]==0) break;
        }
    }
    int time;
    for (int i=1; i<=MAXN/p; i++)
    {
        bool f=0;
        for (int j=1; j<=tot; j++)
        {
            if (i%prime[j]==0) {f=1; break;} time++;
        }
        if (!f) k++;
        if (time>70000000) break;
        if (k==n) {printf("%d\n",p*i); break;}
    }
    if (k<n) printf("0\n");
}





1356: Dream Scape

时间限制: 1 Sec   内存限制: 128 MB
樱狼和另一对樱狼终于解决了飞王里多。但是他们的同时存在也因为世界规则的恢复而变得不合理。于是他们需要逃脱世界规则的制裁。他们来到了他们的梦中之景——哲♂学之殿。(纳尼?!求不黑!!)哲♂学之殿是一个n*n个格子的矩阵。这些格子有些是明亮的,有些是黑暗的。他们只有站在黑暗之中才能逃脱制裁。樱狼和另一对樱狼现在分别站在两个不同的“黑暗矩阵”里。“黑暗矩阵”即一个全部由黑暗格子组成的面积大于1个格子的矩阵。而“不同的‘黑暗矩阵’”指的是两个“黑暗矩阵”没有公共格子。现在小樱和小狼想知道,他们和另一对他们在大殿里有多少种站法。注:因为他们和另一对他们本质上是同一对人,因此不同顺序的站法算作同一种站法。


听大牛说zhw和lh大神是可以在15分钟内水出来的。然而对我来说,并没有什么卵用。为了赶时间,我也只好写得简练一些,争取自己以后可以看懂。首先对于某两个合理的矩形来说,只存在三种情况:存在公共行不存在公共列,存在公共列不存在公共行,二者都不存在(这里的公共边指的不是|_|_|中的中间那列,只能是上下面各两横【请原谅我画不出上面的边,请自行脑补】)
对于这两个矩形,必然存在某以行或某一列将其分割成两部分,我们只要知道那一行或那一列紧靠某一矩形下端或右端时另一部分的矩形数,然后将答案加上紧靠的矩形数乘上另一边的矩形数。当然在二者都不存在的情况下会出现重复,这时候只要减去就行了。
然而要知道这些矩形数目,我们就要知道某个点作为右上右下,左上左下有多少个矩形。这就可以用类似海报覆盖的单调队列维护。嘛这样时间复杂度就可以达到O(n^2)。
好吧我这是写给自己看的所以写的不详细.。
#include <iostream>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <stack>
#define shit 10007
#define N 1003
using namespace std;
int n,high[N],a[N][N],ans[N][N],lu[N][N],LU[N][N],RD[N][N],rd[N][N],ld[N][N],LD[N][N],ru[N][N];
char sta[N];
void changeLR(int mat[N][N])
{
    for (int i=1; i<=n; i++)
        for (int j=1; j<=n/2; j++)
            swap(mat[i][j],mat[i][n-j+1]);
}
void changeUD(int mat[N][N])
{
    for (int i=1; i<=n; i++)
        for (int j=1; j<=n/2; j++)
            swap(mat[j][i],mat[n-j+1][i]);
}
void get(int mat[N][N])
{
    for (int i=1; i<=n; i++)
        for (int j=1; j<=n; j++)
        {
            mat[i][j]+=mat[i-1][j]+mat[i][j-1];
            mat[i][j]-=mat[i-1][j-1];
            mat[i][j]+=shit;
            mat[i][j]%=shit;
        }
}
void Tab(int x[N][N],int y[N][N])
{
    for (int i=1; i<=n; i++) for (int j=1;j<=n; j++) y[i][j]=x[i][j];
}

void solve()
{
    for (int j=0; j<=n; ++j) high[j]=0;
    for (int i=1; i<=n; ++i)
    {
        int area=0; stack<int> s;
        s.push(0);
        for (int j=1; j<=n; ++j) high[j]=high[j]*(a[i][j])+a[i][j];
        for (int j=1; j<=n; ++j)
        {
            while (s.top() && high[s.top()]>high[j])
            {
                int x=s.top();
                s.pop();
                int y=s.top();
                area-=(j-y-1)*(high[x]-max(high[y],high[j]));
            }
            area+=high[j];
            s.push(j);
            ans[i][j]=max(area-1,0) % shit;
        }
    }
}

int main()
{
    scanf("%d",&n);
    for (int i=1; i<=n; ++i)
    {
        scanf("%s",sta);
        for (int j=0; j<n; ++j)
            a[i][j+1]=(sta[j]=='C');
    }

    solve();
    Tab(ans,rd);
    Tab(ans,RD);
    get(RD);

    changeLR(a);
    solve();
    Tab(ans,ld);
    Tab(ans,LD);
    get(LD);

    changeUD(a);
    solve();
    Tab(ans,lu);
    Tab(ans,LU);
    get(LU);

    changeLR(a);
    solve();
    Tab(ans,ru);

    changeUD(a);
    changeUD(ru);
    changeLR(ld),changeLR(LD);
    changeLR(lu),changeUD(lu),changeLR(LU),changeUD(LU);

    int lastans=0;
    for (int i=1; i<=n; i++)
    {
        lastans+=((RD[i][n]-RD[i-1][n]+shit)%shit) * LU[i+1][1];
        lastans%=shit;
        lastans+=((RD[n][i]-RD[n][i-1]+shit)%shit) * LU[1][i+1];
        lastans%=shit;
        for (int j=1; j<=n; j++)
        {
            lastans-=(rd[i][j]*LU[i+1][j+1])%shit,lastans+=shit,lastans%=shit;
            lastans-=(ru[i][j]*LD[i-1][j+1])%shit,lastans+=shit,lastans%=shit;
        }
    }

    printf("%d\n",lastans);
}



于是最后我贴上原题解以资鼓励。

NOIP2013 Day1 

命题学校:衡阳市第八中学

命题人:LMZ(真是对不起ZY了哈)

To The Beginning

1. X0%算法:暴力,祝好运!

2. 100%算法:首先我们可以知道最优方案中,树的移动路线一定不会相交,否则我们交换相交的两棵树剩余的移动路线即可。 

接下来该如何考虑呢? 

我们需要使每行每列有且仅有一棵树,也就是说,没有任意两棵树出在同一行或同一列。移动方案中会涉及到树的水平移动或竖直移动。并且我们发现,对于任意一个方案,我们可以先进行所有的水平移动,然后进行所有的竖直移动,效果是一样的。 那么,水平移动和竖直移动之间有什么联系吗?答案是:没有任何联系!  

于是,我们分别考虑水平移动和竖直移动即可。换言之,我们首先忽视同一列树之间的区别,将树移动到n列,这个过程可以用一个简单的贪心算法达到最优;然后将树按同样的算法移动到n行即可。  

 

时间复杂度:贪心:O(n)   输出:O(n2)

 

 

Synchronicity

1. 60%算法:暴力枚举p的倍数,祝大家拿分愉快,时间复杂度O(p-1)

2. X0%算法:加上隐藏分数(输出0),祝大家拿分愉快。

3. 100%算法:开始找规律---发现循环节

   设C<=P的质数的积,由于P不大,C只是百万级的,硬统计C内有多少个符合要求,设符合要求的个数为c,则答案为((N-1)/c)*C+a[(N-1)%c+1],其中a[i]为第i个符合要求的数,现证明其正确性。

   我们认为,若i合法,则C+i合法。

   现反设C+i非法,则存在p<P满足p|C+i,因为C<=P的质数的积,所以p|C,所以p|i,与假设矛盾,得证。

 

Dream Scape

1. 10%算法:暴力,复杂度O(n6)。写得好一点的话可以写到20分。如果有人写成O(n8)那我也没办法了……

2. X0%算法:我们考虑最终选出的两个黑暗矩形(以下简称为矩形)的位置关系,只可能有以下三种情况:既没有公共行也没有公共列,有公共行没有公共列,没有公共行有公共列。很显然,我们一定可以用一列或者一行将两个矩形隔开。 我们首先枚举每一行,计算底边在这一行的矩形的数量与顶边低于这一行的矩形的数量的乘积。然后枚举每一列,计算右边在这一列的矩形数量与左边在此列右边的矩形数量的乘积。这样的话,对于上述三种情况,case1会被计算两次,case2和case3都只会被计算一次。那么,我们再枚举每个点分别作为矩形的右下角和左下角时,计算case1的方案数,做差即可。 那么,如何快速计算方案数呢? 我们考虑计算每个点作为左上、右上、左下、右下四个区域的矩形数量。那么很容易通过简单的算术运算求出以上需要的方案数了。

之后就爆发出你们各种矩阵DP和暴力的优化以及乱搞的精神吧!

3. 100%算法:下面以计算每个点作为左上的矩形的数量。我们首先O(n2)预处理出每行的每个点向右至多延伸多少个黑色格子。对于点(i,j),记其数量为r(i,j)。那么,以点(i,j)为左上角的矩形数量=∑min(r(k, j), r(i,j))(i<=k<=n)。我们对每一列单独处理。考虑第j列时,我们从第n行枚举到第1行,用一个单调队列维护第i行到第n行的第j列的r(k,j)。每次若r(i,j)比队列头的r(i+1,j)大则直接将r(i,j)插入队列并记录出现1次;否则连续弹出队列头直到小于r(i,j),过程中记录弹出的次数,加1即为r(i,j)出现的次数。再用一个部分和计数即可。容易证明这样做是O(n)的。 类似的方法计算出每个点作为矩形的四个顶点的矩形数量。然后计算前缀和。算出答案就比较容易了。

     时间复杂度:O(n2)

 

 

 


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值