zoukankan      html  css  js  c++  java
  • 【20161109】noip模拟赛

    1.Game

    【题目描述】

    明明和亮亮在玩一个游戏。桌面上一行有n个格子,一些格子中放着棋子。明明和亮亮轮流选择如下方式中的一种移动棋子(图示中o表示棋子,*表示空着的格子):

    1) 当一枚棋子的右边是空格子的话,可以将这枚棋子像右移动一格。

    **o***         ->           ***o**

    2) 当一枚棋子的右边连续两个都有棋子,并且这个棋子往右边数第3格没有棋子,那么可以将这个棋子可以跳过去那两个棋子

    **ooo*        ->           ***oo*

    当任何一枚棋子到达最右边的格子时,这枚棋子自动消失。当一方不能移动时,这方输。假设明明和亮亮都采取最优策略,明明先走,谁将取胜?

     

    【输入数据】

    第一行一个整数T表示数据组数, 0 < T < 10。

    之后T组数据,每组两行,第一行n 表示格子个数,第二行n个字符表示每个格子的情况,o表示有棋子,*表示空着。

     

    【输出数据】

    对于每组数据一个输出,M表示明明赢,L表示亮亮赢。

     

    【样例输入】

    4

    2

    *o

    5

    *o***

    6

    **o**o

    14

    *o***ooo**oo**

     

    【样例输出】

    L

    M

    M

    L

    【数据范围】

    0 <T < 10

    对于50%的数据, n < 20。

    对于100%的数据, n < 1000。 


    第一题就博弈。。跪跪跪。。

    真心不会博弈。。今晚好好重学一遍。。

    看题解好像很好理解???


    Game解题报告

     

    对于前50%的数据,由于n<20,整个棋盘的状态个数 < 2^20。 由于状态数有限,我们可以采取记忆化搜索的办法来实现。

    但对于100%的数据,n的最大可能值达到999,记忆化搜索就不怎么可行了。其实本题有一个更简单的做法:

    考虑每个棋子到最右边格子的距离。把所有棋子这样的距离的总和计为s。我们发现不管选择两种操作中的一种操作,每走一步,s的奇偶性都会发生一次变化。所以说,如果第一次轮到明明时,s是奇数,那么每次轮到明明时s都是奇数。而当s是奇数时,s肯定>0,这时明明总可以走最右边的棋子。也就是说当s为奇数时,总有棋子可以走。所以说,一开始若s为奇数,则明明必胜。同理,若一开始s为偶数,则当亮亮走的时候s总是奇数,所以明明必败。


    贴个代码:

     1 #include<cstdio>
     2 #include<cstdlib>
     3 #include<cstring>
     4 #include<iostream>
     5 using namespace std;
     6 
     7 const int N=25,M=1<<21;
     8 int n,f[2][M],a[N],b[N];
     9 char c[N],cc[N];
    10 
    11 int dfs(int x,int s)
    12 {
    13     if((s&1)!=0) s--;
    14     if(f[x][s]!=-1) return f[x][s];
    15     int ss,ans=0;
    16     for(int i=0;i<n;i++)
    17     {
    18         if((s&(1<<i))!=0 && (i-1>=0 && (s&(1<<(i-1)))==0)) 
    19         {
    20             ss=s-(1<<i)+(1<<(i-1));
    21             if(dfs(1-x,ss)==0) ans=1;
    22         }
    23         if((s&(1<<i))!=0 && (i-1>=0 && (s&(1<<(i-1)))!=0) && (i-2>=0 && (s&(1<<(i-2)))!=0) && (i-3>=0 && (s&(1<<(i-3)))==0)) 
    24         {
    25             ss=s-(1<<i)+(1<<(i-3));
    26             if(dfs(1-x,ss)==0) ans=1;
    27         }
    28     }
    29     f[x][s]=ans;
    30     // printf("f %d %d = %d
    ",x,s,ans);
    31     return ans;
    32 }
    33 
    34 void solve1()
    35 {
    36     scanf("%s",c);
    37     memset(f,-1,sizeof(f));
    38     f[0][0]=f[1][0]=0;
    39     int x=0;
    40     for(int i=0;i<n;i++)
    41     {
    42         if(c[i]=='o') x|=(1<<(n-1-i));
    43     }
    44     // printf("x = %d
    ",x);
    45     if(dfs(0,x)==1) printf("M
    ");
    46     else printf("L
    ");
    47 }
    48 
    49 void solve2()
    50 {
    51     scanf("%s",c+1);
    52     int sum=0;
    53     for(int i=1;i<=n;i++)
    54     {
    55         if(c[i]=='o') sum+=n-i;
    56     }
    57     if(sum%2==0) printf("L
    ");
    58     else printf("M
    ");
    59 }
    60 
    61 int main()
    62 {
    63     // freopen("a.in","r",stdin);
    64     freopen("game.in","r",stdin);
    65     freopen("game.out","w",stdout);
    66     int T,x;
    67     scanf("%d",&T);
    68     while(T--)
    69     {
    70         scanf("%d",&n);
    71         if(n<=20) solve1();
    72         else solve2();
    73     }
    74     return 0;
    75 }
    View Code

    2.Walk

    【题目描述】

    有一块n *n 的土地上,明明和亮亮站在(1,1)处。每块地上写有一个数字a(i, j)。现在他们决定玩一个游戏,每一秒钟,他们俩走向相邻且坐标变大的格子(从(x,y)到(x+1,y)或者从(x,y)到(x,y+1)),他们俩可以按照不同方式来走,最后经过2n-1步到达(n,n)处。明明和亮亮每一秒钟计算他们站的两个位置上数字的差的绝对值,他们希望这些差值的和最大,请问这个最大的和是多少?

     

    【输入数据】

    第一行一个正整数n。

    后面n行,每行n个整数,分别表示每块地上的数字。

     

    【输出数据】

    一个整数,表示最大的差值的和。

     

    【样例输入】

    4

    1 2 3 4

    1 5 3 2

    8 1 3 4

    3 2 1 5

     

    【样例输出】

    13

    【数据范围】

    n <= 100, 每块地上的数字的绝对值不超过300。


    没什么好说的。就直接dp,f[i][j][k]表示走了i步,第一个人的横坐标是j,第二个人的横坐标是k。

    通过走了i步可以算出纵坐标。

    第一维只开了100又跪了。。

     1 #include<cstdio>
     2 #include<cstdlib>
     3 #include<cstring>
     4 #include<iostream>
     5 using namespace std;
     6 
     7 const int N=210;
     8 int n,a[N][N],f[N][N][N];
     9 int dx[2]={0,1};
    10 int dy[2]={1,0};
    11 
    12 int myabs(int x){return x>0 ? x:-x;}
    13 int minn(int x,int y){return x<y ? x:y;}
    14 int maxx(int x,int y){return x>y ? x:y;}
    15 
    16 int main()
    17 {
    18     // freopen("a.in","r",stdin);
    19     freopen("walk.in","r",stdin);
    20     freopen("walk.out","w",stdout);
    21     scanf("%d",&n);
    22     for(int i=0;i<n;i++)
    23         for(int j=0;j<n;j++)
    24             scanf("%d",&a[i][j]);
    25     memset(f,-1,sizeof(f));
    26     f[0][0][0]=0;
    27     int x1,y1,x2,y2,xx1,yy1,xx2,yy2;
    28     for(int i=0;i<=2*n-2;i++)
    29         for(int j=0;j<n;j++)
    30             for(int k=0;k<n;k++)
    31             {
    32                 if(f[i][j][k]==-1) continue;
    33                 // printf("f %d %d %d = %d
    ",i,j,k,f[i][j][k]);
    34                 x1=j;y1=i-j;
    35                 x2=k;y2=i-k;
    36                 for(int ii=0;ii<=1;ii++)
    37                     for(int jj=0;jj<=1;jj++)
    38                     {
    39                         xx1=x1+dx[ii];yy1=y1+dy[ii];
    40                         xx2=x2+dx[jj];yy2=y2+dy[jj];
    41                         if(xx1>=n || yy1>=n || xx2>=n || yy2>=n) continue;
    42                         f[i+1][xx1][xx2]=maxx(f[i+1][xx1][xx2],f[i][x1][x2]+myabs(a[xx1][yy1]-a[xx2][yy2]));
    43                     }
    44             }
    45     printf("%d
    ",f[2*n-2][n-1][n-1]);
    46     return 0;
    47 }
    View Code

    3. Trip

    【题目描述】

    小朋友们出去郊游,明明和亮亮负责在草地上开一个篝火晚会。这个草地你可以认为是又 N * M 块单位长度为1的小正方形的草组成。

    显然有的地方草长的好,有的地方长的不好,坐在上面显然舒服度是不一样的,于是每一块草都有一个舒服度 F。

    现在明明和亮亮要选定一个 a*b 的草场作为晚会的地点,小朋友们就坐在上面,显然他希望小朋友们坐的最舒服!

    不过别急,篝火晚会怎么能少了篝火呢,篝火需要占用 c*d 的草地,当然,篝火必须严格放置在选定的草地的内部,也就是说,篝火的边界不能和选定操场的边界有公共部分,不然学生们怎么围着篝火开晚会呢?

    给定 N*M 大草地每一块的舒服度,寻找一个 a*b 的草地,除去一个严格内部的 c*d 的子草地,使得总的舒服度最大。

     

    【输入数据】

    第1行:6个整数,M ,  N,  b,   a,   d,   c

    第2~N+1行:每行 M 个整数,第 i行j列的整数 Fi,j 表示,第 i行j列的单位草地的舒服度。

     

    【输出数据】

    一个整数,表示最大的舒服值。

     

    【样例输入】

    8 5 5 3 2 1

    1 5 10 3 7 1 2 5

    6 12 4 4 3 3 1 5

    2 4 3 1 6 6 19 8

    1 1 1 3 4 2 4 5

    6 6 3 3 3 2 2 2

     

    【样例输出】

    70

     

     

    【数据说明】

    下面的图片就是对样例的解释,阴影区域就是最佳的选择方案。

     

    比如方案 4 1 4 1 就是显然非法的,因为篝火出现出现在了选定草地的边界,学生们无法严格围住篝火。

     

     

    【数据范围】

    1 ≤ Fi,j ≤ 100

    3 ≤ a ≤ N

    3 ≤ b ≤ M

    1 ≤ c ≤ a-2

    1 ≤ d ≤ b-2

    对于 40% 的数据 N,M ≤ 10

    对于 60% 的数据 N,M ≤ 150

    对于 100% 的数据 N,M ≤ 1000


    这题其实就是求一个矩阵里的最小值。

    然后就可以行做一遍,列做一遍。

    我们可以一行一行的求出每个连续b-d-1个c*d矩形的最小值。再基于这个最小值,一列一列的求出每个a*b大矩形中和最小的c*d矩形。这样我们就可以找到最优的舒服值了。本算法的时间复杂度是O(MN)。

    原本用优先队列。。然后超时了4个点哭。。

    然后用单调队列就巨快了。。orz。。

     1 #include<cstdio>
     2 #include<cstdlib>
     3 #include<cstring>
     4 #include<iostream>
     5 #include<vector>
     6 #include<queue>
     7 using namespace std;
     8 
     9 const int N=1100;
    10 int n,m,A,B,C,D;
    11 int a[N][N],c[N][N],s[N][N],t[N][N],p[N][N],rr[N][N],R[N][N];
    12 struct node{int x,d;}q[N*N];
    13 
    14 int maxx(int x,int y){return x>y ? x:y;}
    15 
    16 
    17 void solve()
    18 {
    19     node k;
    20     int ind,l,r;
    21     for(int i=1;i<=n;i++)
    22     {
    23         l=1;r=0;
    24         for(int j=1;j+D-1<=B-2;j++)
    25         {
    26             k.x=j;k.d=p[i][j];
    27             while(q[r].d>k.d && l<=r) r--;
    28             q[++r]=k;
    29             ind=j;
    30         }
    31         for(int j=1;j+B-2<=m;j++)
    32         {
    33             while(q[l].x<j) l++;
    34             rr[i][j]=q[l].d;
    35             ind++;k.x=ind;k.d=p[i][ind];
    36             while(q[r].d>k.d && l<=r) r--;
    37             q[++r]=k;
    38         }
    39     }
    40     for(int i=1;i<=m;i++)
    41     {
    42         l=1;r=0;
    43         for(int j=1;j+C-1<=A-2;j++)
    44         {
    45             k.x=j;k.d=rr[j][i];
    46             while(q[r].d>k.d && l<=r) r--;
    47             q[++r]=k;
    48             ind=j;
    49         }
    50         for(int j=1;j+A-2<=n;j++)
    51         {
    52             while(q[l].x<j) l++; 
    53             R[j][i]=q[l].d;
    54             ind++;k.x=ind;k.d=rr[ind][i];
    55             while(q[r].d>k.d && l<=r) r--;
    56             q[++r]=k;
    57         }
    58     }
    59     int ans=0;
    60     for(int i=1;i+A-1<=n;i++)
    61         for(int j=1;j+B-1<=m;j++)
    62             ans=maxx(ans,t[i][j]-R[i+1][j+1]);
    63     printf("%d
    ",ans);
    64 }
    65 
    66 int main()
    67 {
    68     // freopen("a.in","r",stdin);
    69     freopen("trip.in","r",stdin);
    70     freopen("trip.out","w",stdout);
    71     scanf("%d%d%d%d%d%d",&m,&n,&B,&A,&D,&C);
    72     for(int i=1;i<=n;i++)
    73         for(int j=1;j<=m;j++)
    74             scanf("%d",&a[i][j]);
    75     memset(s,0,sizeof(s));
    76     for(int i=1;i<=n;i++)
    77         for(int j=1;j<=m;j++)
    78         {
    79             s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j];
    80         }
    81     for(int i=1;i+A-1<=n;i++)
    82         for(int j=1;j+B-1<=m;j++)
    83             t[i][j]=s[i+A-1][j+B-1]-s[i+A-1][j-1]-s[i-1][j+B-1]+s[i-1][j-1];
    84     for(int i=1;i+C-1<=n;i++)
    85         for(int j=1;j+D-1<=m;j++)
    86             p[i][j]=s[i+C-1][j+D-1]-s[i+C-1][j-1]-s[i-1][j+D-1]+s[i-1][j-1];
    87     solve();
    88     return 0;
    89 }

    点分治裸题。。

    先找出树的重心,对于每个点维护一个到树的重心的乘积d[x]。

    然后找经过树的重心的树链是否有乘积为k的。

    然后分治算各个子树。

    ps:学了奥爷爷的线性求逆元。。强啊。。

      1 #include<cstdio>
      2 #include<cstdlib>
      3 #include<cstring>
      4 #include<iostream>
      5 #include<vector>
      6 #include<queue>
      7 using namespace std;
      8 
      9 typedef long long LL;
     10 const int N=1000100,M=1000010,mod=1000003,INF=(int)1e9;
     11 int n,len,sl,tl,a1,a2;
     12 LL K,d[N],val[N],t[N],s[N],v[M],ny[M];
     13 int first[N],size[N],mark[N],id[M];
     14 struct node{
     15     int x,y,next;
     16 }a[2*N];
     17 
     18 int minn(int x,int y){return x<y ? x:y;}
     19 
     20 LL quickpow(LL x,LL y)
     21 {
     22     LL ans=1;
     23     while(y)
     24     {
     25         if(y&1) ans=ans*x%mod;
     26         x=x*x%mod;
     27         y/=2;
     28     }
     29     return ans;
     30 }
     31 
     32 int ins(int x,int y)
     33 {
     34     a[++len].x=x;a[len].y=y;
     35     a[len].next=first[x];first[x]=len;
     36 }
     37 
     38 void find_root(int x,int fa,int tot,int &root)
     39 {
     40     size[x]=1;
     41     bool bk=1;
     42     for(int i=first[x];i;i=a[i].next)
     43     {
     44         int y=a[i].y;
     45         if(mark[y] || y==fa) continue;
     46         find_root(y,x,tot,root);
     47         size[x]+=size[y];
     48         if(2*size[y]>tot) bk=0;
     49     }
     50     if(bk && 2*(tot-size[x])<=tot) root=x;
     51 }
     52 
     53 void DFS(int x,int fa,int root)
     54 {
     55     d[x]=d[fa]*val[x]%mod;
     56     t[++tl]=d[x];id[tl]=x;
     57     LL now=(ny[d[x]]*K%mod)*val[root]%mod;
     58     if(v[now]) 
     59     {
     60         int X=x,Y=v[now];
     61         if(X>Y) swap(X,Y);
     62         if(X<a1) a1=X,a2=Y;
     63         else if(X==a1 && Y<a2) a2=Y;
     64     }
     65     size[x]=1;
     66     for(int i=first[x];i;i=a[i].next)
     67     {
     68         int y=a[i].y;
     69         if(mark[y] || y==fa) continue;
     70         DFS(y,x,root);
     71         size[x]+=size[y];
     72     }
     73 }
     74 
     75 int dfs(int x,int tot)
     76 {
     77     find_root(x,0,tot,x);
     78     // printf("tot = %d  root = %d
    ",tot,x);
     79     mark[x]=1;
     80     sl=0;s[++sl]=val[x];
     81     d[x]=val[x];
     82     for(int i=first[x];i;i=a[i].next)
     83     {
     84         int y=a[i].y;
     85         if(mark[y]==1) continue;
     86         tl=0;
     87         DFS(y,x,x);
     88         for(int j=1;j<=tl;j++)
     89         {
     90             s[++sl]=t[j];
     91             if(v[t[j]]==0) v[t[j]]=id[j];
     92             else v[t[j]]=minn(v[t[j]],id[j]);
     93         }
     94     }
     95     if(v[K]) 
     96     {
     97         int X=x,Y=v[K];
     98         if(X>Y) swap(X,Y);
     99         if(X<a1) a1=X,a2=Y;
    100         else if(X==a1 && Y<a2) a2=Y;
    101     }
    102     for(int i=1;i<=sl;i++) v[s[i]]=0;
    103     for(int i=first[x];i;i=a[i].next)
    104     {
    105         int y=a[i].y;
    106         if(mark[y]==1) continue;
    107         dfs(y,size[y]);
    108     }
    109 }
    110 
    111 int main()
    112 {
    113     // freopen("a.in","r",stdin);
    114     freopen("multik.in","r",stdin);
    115     freopen("multik.out","w",stdout);
    116     scanf("%d%d",&n,&K);
    117     len=0;a1=INF;a2=INF;
    118     memset(v,0,sizeof(v));
    119     memset(mark,0,sizeof(mark));
    120     memset(first,0,sizeof(first));
    121     ny[1]=1;
    122     for(int i=2;i<=mod;i++) 
    123         ny[i]=(mod-(mod/i))*ny[mod%i]%mod;
    124         // ny[i]=quickpow(i,mod-2);
    125     for(int i=1;i<=n;i++)  
    126         scanf("%d",&val[i]);
    127     for(int i=1;i<n;i++)
    128     {
    129         int x,y;
    130         scanf("%d%d",&x,&y);
    131         ins(x,y);
    132         ins(y,x);
    133     }
    134     dfs(1,n);
    135     if(a1<INF) printf("%d %d
    ",a1,a2);
    136     else printf("No solution
    ");
    137     return 0;
    138 }
  • 相关阅读:
    一个简单的knockout.js 和easyui的绑定
    knockoutjs + easyui.treegrid 可编辑的自定义绑定插件
    Knockout自定义绑定my97datepicker
    去除小数后多余的0
    Windows Azure Web Site (15) 取消Azure Web Site默认的IIS ARR
    Azure ARM (1) UI初探
    Azure Redis Cache (3) 创建和使用P级别的Redis Cache
    Windows Azure HandBook (7) 基于Azure Web App的企业官网改造
    Windows Azure Storage (23) 计算Azure VHD实际使用容量
    Windows Azure Virtual Network (11) 创建VNet-to-VNet的连接
  • 原文地址:https://www.cnblogs.com/KonjakJuruo/p/6048138.html
Copyright © 2011-2022 走看看