XTU Monthly, April 2014(湘潭大学4月月赛)

来源:互联网 发布:docker nginx 镜像 编辑:程序博客网 时间:2024/05/21 06:57

比赛链接:

http://202.197.224.59/OnlineJudge2/index.php/Contest/problems/contest_id/31


^_^ 中文题目就不说题目意思啦。


Problem A  Number (XTU 1192)

题目链接:

http://202.197.224.59/OnlineJudge2/index.php/Problem/read/id/1192

解题思路:

贪心。

如果n是偶数,直接除以2,如果是奇数,看+1还是-1所得数的2的因子个数多。3另外单独处理(是减一,不是加一)。

代码:

#include<iostream>#include<cmath>#include<cstdio>#include<sstream>#include<cstdlib>#include<string>#include<string.h>#include<cstring>#include<algorithm>#include<vector>#include<map>#include<set>#include<stack>#include<list>#include<queue>#include<ctime>#include<bitset>#define eps 1e-6#define INF 0x3f3f3f3f#define PI acos(-1.0)#define ll __int64#define LL long long#define lson l,m,(rt<<1)#define rson m+1,r,(rt<<1)|1#define M 1000000007//#pragma comment(linker, "/STACK:1024000000,1024000000")using namespace std;ll n;int Ti(ll cur) //求出cur中因子2的个数{    int cnt=0;    while(cur%2==0&&cur)    {        cnt++;        cur/=2;    }    return cnt;}int main(){    while(~scanf("%I64d",&n))    {        int ans=0;        while(n)        {            ans++;            if(n%2==0) //是偶数直接除以2            {                n/=2;                continue;            }            if(n==3||n==1)            {                n--;                continue;            }            int a=Ti(n+1),b=Ti(n-1);            if(a>b) //如果是+1 得到2的次数多                n++;            else  //如果次数相等或小于 选择小的                n--;        }        printf("%d\n",ans);    }   return 0;}

Problem B  Lukey Wheel (XTU 1193)

题目链接:

http://202.197.224.59/OnlineJudge2/index.php/Problem/read/id/1193

解题思路:

概率dp.求期望+预处理。

至上限x=1000,y=1000. 从后往前考虑,dp[i][j]:表示已经获得i个一等奖,j个二等奖,到达获得1000个一等奖1000个二等奖还需转动次数的期望。

显然当i=1000时,dp[i][j]=1/8*(1+dp[i][j+1])+7/8*(1+dp[i][j])  也即:dp[i][j]=1/8*dp[i][j+1]+8 一等奖已经是上限了,再获得一等奖无贡献(即本身dp[i][j])。

同理当j=1000时,dp[i][j]=1/8*(1+dp[i+1][j])+7/8*(1+dp[i+1][j]) 也即:dp[i][j]=1/8*dp[i+1][j]+8 二等奖次数已达到上限,再获得二等奖无贡献(即本身dp[i][j])。

当i<1000&&j<1000时 dp[i][j]=1/8*(1+dp[i+1][j])+1/8*(1+dp[i][j+1]+6/8(1+dp[i][j]) 当前这次的转动要么是一等奖,要么是二等奖,要么都不是(即本身dp[i][j])。

输入一个x,y,直接输出dp[1000-x][1000-y]即可。

代码:

//#include<CSpreadSheet.h>#include<iostream>#include<cmath>#include<cstdio>#include<sstream>#include<cstdlib>#include<string>#include<string.h>#include<cstring>#include<algorithm>#include<vector>#include<map>#include<set>#include<stack>#include<list>#include<queue>#include<ctime>#include<bitset>#define eps 1e-6#define INF 0x3f3f3f3f#define PI acos(-1.0)#define ll __int64#define LL long long#define lson l,m,(rt<<1)#define rson m+1,r,(rt<<1)|1#define M 1000000007//#pragma comment(linker, "/STACK:1024000000,1024000000")using namespace std;#define Maxn 1100double dp[Maxn][Maxn];int main(){   int x,y;   x=1000;   y=1000;      dp[x][y]=0;      for(int j=y-1;j>=0;j--)          dp[x][j]=8+dp[x][j+1];      for(int j=x-1;j>=0;j--)          dp[j][y]=8+dp[j+1][y];       for(int i=x-1;i>=0;i--)       {           for(int j=y-1;j>=0;j--)               dp[i][j]=1.0/2.0*(1+dp[i+1][j])+1.0/2.0*(1+dp[i][j+1])+3;       }   while(~scanf("%d%d",&x,&y))       printf("%.6lf\n",dp[1000-x][1000-y]);   return 0;}


Problem C A+B (XTU 1194)

题目链接:

http://202.197.224.59/OnlineJudge2/index.php/Problem/read/id/1194

解题思路:

裸的扩展欧几里得算法。

显然题目要求a*x+b*y=n,正整数<x,y>的对数。

分享一篇写的比较好的扩展欧基里德算法文章:http://chhaj5236.blog.163.com/blog/static/112881081200942542255916/

设k=gcd(a,b)

显然如果n不能被k整除,无解。方程可化简为a*x/(n/k)+b*y/(n/k)=gcd(a,b)=gcd(b,a%b)=b*x2+(a-a/b*b)*y2

x=n/k*x1 y=n/k*y1  x1=y2 y1=x2-a/b*y2

a,b,n同时除以gcd(a,b).

求出最小的正整数解x,a(x+p*b)+b*(y-p*a)=n. 只要满足a(x+p*b)<=n即可,也即p<=(n/a-x)/b 详细请见代码。

代码:

//#include<CSpreadSheet.h>#include<iostream>#include<cmath>#include<cstdio>#include<sstream>#include<cstdlib>#include<string>#include<string.h>#include<cstring>#include<algorithm>#include<vector>#include<map>#include<set>#include<stack>#include<list>#include<queue>#include<ctime>#include<bitset>#define eps 1e-6#define INF 0x3f3f3f3f#define PI acos(-1.0)#define ll __int64#define LL long long#define lson l,m,(rt<<1)#define rson m+1,r,(rt<<1)|1#define M 1000000007//#pragma comment(linker, "/STACK:1024000000,1024000000")using namespace std;ll ex_gcd(ll a,ll b,ll &x,ll &y) //扩展欧几里德算法{    if(!b)    {        x=1;        y=0;        return a;    }    ll g=ex_gcd(b,a%b,x,y);    ll temp=x;    x=y;    y=temp-a/b*y;    return g;}int main(){   ll a,b,n;   while(~scanf("%I64d%I64d%I64d",&a,&b,&n))   {       if(a==b)       {           if(n%a==0)               printf("1\n");           else               printf("0\n");           continue;       }       ll x,y,bb;       ll gg=ex_gcd(a,b,x,y);       if(n%gg) //无解       {           printf("0\n");           continue;       }       x=x*n/gg; //求出一个解       y=y*n/gg;       b=b/gg;       a=a/gg;       n/=gg;       x=x%b;       if(x<0)  //求出x的最小的正整数解           x+=b;       ll temp=(n-x*a)/b; //求出y       //printf("::x:%I64d y:%I64d\n",x,temp);       if(temp<0)  //此时y如果小于0的话,肯定不存在都是正整数的解       {           printf("0\n");           continue;       }       printf("%d\n",(n/a-x)/b+1); //推出求个数的式子   }   return 0;}

Problem D Hexa Kill

题目链接:

http://202.197.224.59/OnlineJudge2/index.php/Problem/read/id/1195

解题思路:

拓扑排序+状压dp

先拓扑排序判断依赖关系是否有环。

dp[i]表示在走了状态为i的节点后,需要的最小的能量能量消化。预处理出每个节点的前驱(压缩成一个整数)。

dp[i|(1<<j)]=min(dp[i|(1<<j)],dp[i]+en[j][num[i]+1];

num[i]表示状态为i时,节点个数。

^-^-^  用二维矩阵保存就有问题,用vector就没问题,不知道什么原因,我怀疑这个oj的数据有问题。

代码:

//#include<CSpreadSheet.h>#include<iostream>#include<cmath>#include<cstdio>#include<sstream>#include<cstdlib>#include<string>#include<string.h>#include<cstring>#include<algorithm>#include<vector>#include<map>#include<set>#include<stack>#include<list>#include<queue>#include<ctime>#include<bitset>#define eps 1e-6#define INF 0x3f3f3f3f#define PI acos(-1.0)#define ll __int64#define LL long long#define lson l,m,(rt<<1)#define rson m+1,r,(rt<<1)|1#define M 1000000007//#pragma comment(linker, "/STACK:1024000000,1024000000")using namespace std;#define Maxn 21int n,m;int in[Maxn],dp[1<<Maxn],en[Maxn][Maxn];int num[1<<Maxn];int be[Maxn];queue<int>myq;vector<int>myv[Maxn];//bool hav[Maxn][Maxn];bool Tp() //拓扑排序判断是否有环{    while(!myq.empty())        myq.pop();    for(int i=1;i<=n;i++)        if(!in[i])            myq.push(i);    int cnt=0;    while(!myq.empty())    {        int temp=myq.front();        myq.pop();        cnt++;       /* for(int i=1;i<=n;i++)        {            if(!in[i]||!hav[temp][i])                continue;            in[i]--;            if(!in[i])                myq.push(i);        }*/        for(int i=0;i<myv[temp].size();i++)        {            if(!(--in[myv[temp][i]]))                myq.push(myv[temp][i]);        }    }    if(cnt==n)        return true;    return false;}int main(){   //freopen("in.txt","r",stdin);   //freopen("out.txt","w",stdout);   //printf("%d\n",(1<<18)*18);   //system("pause");   while(~scanf("%d%d",&n,&m))   {       memset(in,0,sizeof(in));       memset(be,0,sizeof(be));       memset(num,0,sizeof(num));       //memset(hav,false,sizeof(hav));       for(int i=1;i<=n;i++)       {           for(int j=1;j<=n;j++) //en[i][j]表示第i中材料第j步放需要的能量           {               scanf("%d",&en[i][j]);               //hav[i][j]=false;           }           myv[i].clear();       }       for(int i=1;i<=m;i++)       {           int x,y;           scanf("%d%d",&x,&y);           in[x]++;           be[x]|=(1<<(y-1)); //第x中材料的前驱材料状态           //hav[y][x]=true;           myv[y].push_back(x);       }       if(!Tp()) //有环       {           printf("Enemy Hexa Kill\n");           continue;       }       memset(dp,INF,sizeof(dp));       dp[0]=0;       for(int i=0;i<(1<<n);i++) //       {           for(int j=1;j<=n;j++) //           {               if(dp[i]==INF) //当前状态不满足                  continue;               if((1<<(j-1))&i) //没有走到                  continue;               if((be[j]&i)==be[j]) //前驱状态满足               {                   if(dp[i]+en[j][num[i]+1]<dp[i|(1<<(j-1))])                   {                       dp[i|(1<<(j-1))]=dp[i]+en[j][num[i]+1];                       num[i|(1<<(j-1))]=num[i]+1; //个数                   }               }           }       }       printf("%d\n",dp[(1<<n)-1]);   }   return 0;}/*5 52 3 2 1 11 2 1 3 32 1 3 3 31 2 1 2 11 1 2 1 12 32 45 41 21 5*/


Problem E Tree

题目链接:

http://202.197.224.59/OnlineJudge2/index.php/Problem/read/id/1196

解题思路: 

DFS+线段树(区间更新、单点查询)

对于节点u,增加d,它对子树节点v的影响是,(dep[v]-dep[u]+1)*d  化简得d*dep[v]+(1-dep[u])*d 显然对于每个点v,dep[v]是确定的,只用维护一个一次函数的两个系数即可。

每次更新时,只用更新子树节点区间的这个两个系数。

一遍DFS编号,确定每个节点所维护的区间范围,线段树更新。


=_=  =_= 不知道为什么用vector就RE,用邻接表就可以过,求大神指正。

代码:

//#include<CSpreadSheet.h>#include<iostream>#include<cmath>#include<cstdio>#include<sstream>#include<cstdlib>#include<string>#include<string.h>#include<cstring>#include<algorithm>#include<vector>#include<map>#include<set>#include<stack>#include<list>#include<queue>#include<ctime>#include<bitset>#define eps 1e-6#define INF 0x3f3f3f3f#define PI acos(-1.0)#define ll __int64#define LL long long#define lson l,m,(rt<<1)#define rson m+1,r,(rt<<1)|1#define M 1000000007//#pragma comment(linker, "/STACK:1024000000,1024000000")using namespace std;#define Maxn 110000vector<int>myv[Maxn];int to[Maxn],n,dep[Maxn],dd,q,from[Maxn];ll ta,tb;int cnt;struct Node  //维护两个系数{    ll a,b;}node[Maxn<<2];struct Edge  //邻接表建树{    int v;    struct Edge * next;}edge[Maxn],*head[Maxn];void add(int a,int b) //添加边{    ++cnt;    edge[cnt].v=b;    edge[cnt].next=head[a];    head[a]=&edge[cnt];}void build(int l,int r,int rt){    node[rt].a=node[rt].b=0;    if(l==r)        return ;    int m=(l+r)>>1;    build(lson);    build(rson);}void pushdown(int rt){    if(node[rt].a)    {        node[rt<<1].a+=node[rt].a;        node[rt<<1|1].a+=node[rt].a;        node[rt].a=0;    }    if(node[rt].b)    {        node[rt<<1].b+=node[rt].b;        node[rt<<1|1].b+=node[rt].b;        node[rt].b=0;    }}int dfs(int cur,int hi) //返回子树的最大编号  ,求出每个节点维护的区间{    ++dd;    from[cur]=to[cur]=dd; //编号    dep[cur]=hi; //深度    struct Edge * p=head[cur];    //for(int i=0;i<myv[cur].size();i++)    while(p)    {        int temp=dfs(p->v,hi+1);        to[cur]=max(temp,to[cur]);        p=p->next;    }    return to[cur];}void query(int x,int l,int r,int rt) //单点查询{    if(l==r)    {        ta=node[rt].a; //返回最终的两个系数        tb=node[rt].b;        return ;    }    pushdown(rt);    int m=(l+r)>>1;    if(x<=m)        query(x,lson);    else        query(x,rson);}void update(int L,int R,ll va,ll vb,int l,int r,int rt) //更新区间的系数值{    if(L<=l&&R>=r)    {        node[rt].a+=va;        node[rt].b+=vb;        return ;    }    int m=(l+r)>>1;    pushdown(rt);    if(L<=m)        update(L,R,va,vb,lson);    if(R>m)        update(L,R,va,vb,rson);}int main(){   //freopen("in.txt","r",stdin);   //freopen("out.txt","w",stdout);   int t;   scanf("%d",&t);   while(t--)   {       scanf("%d",&n);      /* for(int i=0;i<=n;i++)            myv[i].clear();*/       int tt;       cnt=0;       memset(head,NULL,sizeof(head));       for(int i=1;i<=n;i++)       {           int temp;           scanf("%d",&temp);           //myv[temp].push_back(i);           add(temp,i);           if(temp==0)             tt=i;       }       dd=0;       dfs(tt,1);       build(1,n,1);       scanf("%d",&q);       while(q--)       {           ll ord,x,d;           scanf("%I64d",&ord);           if(ord==2)           {               scanf("%I64d",&x);               //x=from[x];               //printf("x:%I64d %d\n",x,dep[x]);               query(from[x],1,n,1); //单点查询               printf("%I64d\n",ta*dep[x]+tb);           }           else           {               scanf("%I64d%I64d",&x,&d);               //printf("x:%I64d %d\n",x,dep[x]);               update(from[x],to[x],d,d*(1-dep[x]),1,n,1); //区间更新           }       }   }   return 0;}




0 0
原创粉丝点击