Codeforces #292 Div 1 简要题解

来源:互联网 发布:洛阳 知乎 编辑:程序博客网 时间:2024/05/21 06:40

A. Drazil and Factorial(516A)

题目链接

http://codeforces.com/contest/516/problem/A

题目大意

定义F(a)为数字a的每一位数字的阶乘之积,要你找出一个最大的数字x,使得F(x)=F(a)

思路

可以找到下面的规律:
0!(无视掉)
1!(无视掉)
2!=2!
3!=3!
4!=2!2!3!
5!=5!
6!=5!3!
7!=7!
8!=7!2!2!2!
9!=7!3!3!2!

其中2 3 5 7均未变,显然在同样的F值下,用4 6 8 9不如拆成2 3 5 7来用更划算,因此最终的答案的每一位也显然只会出现2 3 5 7,因此我们可以对原来的F(a)分解质因数,然后先尽量用光7!,再尽量用光5!,依次类推,就可以得到最大的答案了

代码

#include <iostream>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <algorithm>using namespace std;int n,fact[25],t[25];char s[25];void cal(int num,int val) //val=1是添加质因数,-1是减少质因数{    for(int x=2;x*x<=num;x++)        while(num%x==0)        {            num/=x;            t[x]+=val;        }    if(num>1) t[num]+=val;}int main(){    scanf("%d",&n);    fact[1]=1;    for(int i=2;i<=9;i++) fact[i]=fact[i-1]*i;    scanf("%s",s+1);    for(int i=1;i<=n;i++)        cal(fact[s[i]-'0'],1);    while(t[7]) cal(fact[7],-1),printf("7");    while(t[5]) cal(fact[5],-1),printf("5");    while(t[3]) cal(fact[3],-1),printf("3");    while(t[2]) cal(fact[2],-1),printf("2");    printf("\n");    return 0;}

B. Drazil and Tiles(516B)

题目链接

http://codeforces.com/contest/516/problem/B

题目大意

给一个带有障碍物的nm大小的棋盘放入若干个12大小的瓦片,问是否存在且仅存在一种使得整个棋盘被障碍物和瓦片完全覆盖,瓦片和障碍物、瓦片和瓦片均不重叠的方案,并输出这种方案。

思路

这个题目的做法真的非常有意思,值得学习
首先我们把每个空格当成一个结点,并求出每个空格的度数(相邻的空格个数),然后我们首先将初始时度数为1的点入队,每次从队首取出一个空格a,由于这个点一定是度数为1的,因此我们找出那个唯一的与它相邻的空格b,并在这两个空格上放上一个瓦片,标记这两个空格均访问过,更新与点b相邻的所有的空格c的度数-1,若找到某个c更新后度数为1,则再将这个c入队。
若最终的棋盘里仍有空格,那么表明无可行方案或有多种可行方案。

考虑两种不合法情况:
1、无可行方案。那么显然上面的做法无法覆盖掉所有的棋盘。
2、有多种方案。由于每次我们只选取度数为1的点a入队,这些点找到的相邻的空格b也是唯一的。若存在多种方案,则一定是在操作过程中,出现了某些度数大于1的点,使得它们无论如何也无法被加入到队列中,被瓦片覆盖。而假如我们对棋盘进行黑白染色,这些最终空着的格子可以构成若干个联通块,在每个联通块内,空格度数均大于等于二,黑白格子个数相等。

实际上这个题可以建立如下一个模型:
给定一个无向图,要你对图中所有的边进行黑白染色,使得最终每个点都只连接一条黑边,问是否存在且仅存在一种合法方案。

代码

#include <iostream>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <algorithm>#include <queue>#define MAXN 2100using namespace std;typedef pair<int,int> pr;char map[MAXN][MAXN];int outDegree[MAXN][MAXN];int n,m;char s[MAXN];int xx[]={1,-1,0,0},yy[]={0,0,1,-1};char tag[4][5]={"^v","v^","<>","><"};queue<pr>q;bool vis[MAXN][MAXN];void NotUnique(){    cout<<"Not unique\n"<<endl;    exit(0);}int tot=0;bool inMap(int x,int y){    if(x<1||x>n||y<1||y>m) return false;    if(map[x][y]!='.') return false;    return true;}int main(){    scanf("%d%d",&n,&m);    for(int i=1;i<=n;i++)        scanf("%s",map[i]+1);    for(int i=1;i<=n;i++)        for(int j=1;j<=m;j++)        {            if(map[i][j]!='.') continue;            for(int dir=0;dir<4;dir++)            {                int newi=i+xx[dir],newj=j+yy[dir];                if(!inMap(newi,newj)) continue;                outDegree[i][j]++;            }            if(outDegree[i][j]==0) NotUnique();            if(outDegree[i][j]==1) q.push(make_pair(i,j));        }    while(!q.empty())    {        bool find=false;        pr now=q.front();        q.pop();        if(map[now.first][now.second]!='.') continue;        for(int dir=0;dir<4;dir++)        {            int newx=now.first+xx[dir],newy=now.second+yy[dir];            if(!inMap(newx,newy)) continue;            map[now.first][now.second]=tag[dir][0];            map[newx][newy]=tag[dir][1];            for(int dir1=0;dir1<4;dir1++)            {                int nextx=newx+xx[dir1],nexty=newy+yy[dir1];                if(inMap(nextx,nexty))                {                    outDegree[nextx][nexty]--;                    if(outDegree[nextx][nexty]==1)                    {                        q.push(make_pair(nextx,nexty));                    }                }            }            break; //!!!!!!        }    }    bool flag=true;    for(int i=1;i<=n;i++)        for(int j=1;j<=m;j++)            if(map[i][j]=='.')            {                flag=false;                break;            }    if(!flag) NotUnique();    for(int i=1;i<=n;i++)        printf("%s\n",map[i]+1);    return 0;}

C. Drazil and Park(516C)

题目链接

http://codeforces.com/contest/516/problem/C

题目大意

在一个环形跑道上种着n个树,给出每个树的高度以及两个相邻的树之间的距离,定义从树a跑到树b的代价是2(ha+hb)+dista,bm次询问,每次标记某段区间里的树不能使用,问从一个树跑到另一个树的代价最大值。

思路

首先把这个长度为n的环断开变成长度为2n的链,那么就是在这个链上询问某个区间内最大的2(ha+hb)+dista,b值,这个问题非常类似于最大子段和问题,最大子段和问题就是用线段树维护两个标记Lmax,Rmax,分别代表每个区间由左端点向右延伸的最大值、由右端点向左延伸的最大值。还要维护sum,maxsum标记,分别代表该区间长度以及该区间的最大子段和。

在这个问题里多了个h值,我们可以让Lmax代表每个区间由左端点向右延伸的最大值+左端点的h值,让Rmax代表每个区间由右端点向左延伸的最大值+右端点的h值,这样维护出的maxsum标记就能代表该区间最大子段和+最大子段两端的h

代码

#include <iostream>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <algorithm>#define MAXN 210000#define INF 0x3f3f3f3fusing namespace std;typedef long long int LL;struct Info{    LL Lmax,Rmax,sum,maxsum;}info[MAXN<<2],ans;int n,m;LL d[MAXN],h[MAXN];void pushup(Info &o,Info lc,Info rc,LL len){    o.Lmax=max(lc.Lmax,lc.sum+len+rc.Lmax);    o.Rmax=max(rc.Rmax,rc.sum+len+lc.Rmax);    o.sum=lc.sum+len+rc.sum;    o.maxsum=max(max(lc.maxsum,rc.maxsum),lc.Rmax+len+rc.Lmax);}void Build(int o,int L,int R){    if(L==R)    {        info[o].Lmax=info[o].Rmax=2*h[L];        return;    }    int M=(L+R)>>1;    Build(o<<1,L,M);    Build(o<<1|1,M+1,R);    pushup(info[o],info[o<<1],info[o<<1|1],d[M]);}void query(int o,int L,int R,int ql,int qr){    if(ql<=L&&R<=qr)    {        if(ql==L) ans=info[o];        else pushup(ans,ans,info[o],d[L-1]);        return;    }    int M=(L+R)>>1;    if(ql<=M) query(o<<1,L,M,ql,qr);    if(qr>M) query(o<<1|1,M+1,R,ql,qr);}int main(){    scanf("%d%d",&n,&m);    for(int i=1;i<=n;i++)    {        scanf("%I64d",&d[i]);        d[i+n]=d[i];    }    for(int i=1;i<=n;i++)    {        scanf("%I64d",&h[i]);        h[i+n]=h[i];    }    Build(1,1,n*2);    for(int i=1;i<=m;i++)    {        int x,y,L,R;        scanf("%d%d",&L,&R);        if(L>R)        {            x=R+1;            y=L-1;        }        else        {            x=R+1;            y=n+L-1;        }        query(1,1,2*n,x,y);        printf("%I64d\n",ans.maxsum);    }    return 0;}
0 0
原创粉丝点击