2016 acm/icpc 沈阳现场赛题解(5道题,更新ing)

5948.Thickest Burger(签到题)

http://acm.hdu.edu.cn/showproblem.php?pid=5948

题目大意:

给你A和B,问2A+B和2B+A谁大?

题目分析:

略。

#include <bits/stdc++.h>
using namespace std;
int T,a,b;
int main() {
    scanf("%d",&T);
    while(t--) {
        scanf("%d%d",&a,&b);
        printf("%d\n", max(a*2+b,b*2+a));
    }

}

5949. Relative atomic mass (签到题)

http://acm.hdu.edu.cn/showproblem.php?pid=5949

题目大意:

输入一个由C H O组成的分子式,求相对分子质量。

题目分析:

略。(实在不知道为什么这么水的两道题会出现在ACM竞赛中。)

#include <bits/stdc++.h>
using namespace std;
#define RE(x) freopen(x,"r",stdin)
#define WR(x) freopen(x,"w",stdout)
typedef long long ll;
int T;
char s[15];
int main() {
    scanf("%d",&T);
    while(T--) {
        scanf("%s",s);
        int ans=0;
        for(int i=0;i<strlen(s);i++) {
            if(s[i]=='H') ans+=1;
            else if(s[i]=='O') ans+=16;
            else if(s[i]=='C') ans+=12;
        }
        printf("%d\n",ans );
    }
}

5950. Recursive sequence(矩阵快速幂解递推公式)

http://acm.hdu.edu.cn/showproblem.php?pid=5950

题目大意:

已知 f1=a,f2=b,fn=2fn2+fn1+n4 ,求 fn%M .

题目分析:

这道题直接推肯定是不可能的,因为n的范围到了 231 ,这个解法我也是头一次见到,是用矩阵乘法去推一个数列的第n项。

这篇文章中介绍了这种常系数线性递推式的矩阵构造方法:http://www.cnblogs.com/wmrv587/p/3965424.html

f(n)f(n1)n4n3n2n1=Af(n1)f(n2)(n1)4(n1)3(n1)2n11

Xn=Axn1 ,所以 Xn=An2X2 ,而 X2 显然是已知的。

问题转化为构造这个矩阵A,根据递推公式,很容易推出来这个矩阵A长这个样子:

1100000200000010100004041000606310040432101011111

接下来就是套快速矩阵乘法的模板了,复杂度仅为 O(logn) .

#include <bits/stdc++.h>
using namespace std;
#define RE(x) freopen(x,"r",stdin)
#define WR(x) freopen(x,"w",stdout)
typedef long long ll;
ll M = 2147493647L;
class Matrix{
public:
    ll mat[7][7]={
        {1,2,1,4,6,4,1},
        {1,0,0,0,0,0,0},
        {0,0,1,4,6,4,1},
        {0,0,0,1,3,3,1},
        {0,0,0,0,1,2,1},
        {0,0,0,0,0,1,1},
        {0,0,0,0,0,0,1}
    };
    Matrix operator*(const Matrix& m)const{
        Matrix tmp;
        for(int i = 0 ; i < 7; i++){
            for(int j = 0 ; j < 7 ; j++){
                tmp.mat[i][j] = 0;
                for(int k = 0 ; k < 7 ; k++){
                    tmp.mat[i][j] += mat[i][k]*m.mat[k][j]%M;
                    tmp.mat[i][j] %= M;
                }
            }
        }
        return tmp;
    }

};
Matrix Pow(Matrix &m , int k){
    Matrix ans;
    memset(ans.mat , 0 , sizeof(ans.mat));
    for(int i = 0 ; i < 7 ; i++)
        ans.mat[i][i] = 1;
    while(k){
        if(k&1)
            ans = ans*m;
        k >>= 1;
        m = m*m;
    }
    return ans;
}
int T;
int main() {
    int T;
    ll a,b,n;
    scanf("%d",&T);
    while(T--) {
        scanf("%I64d %I64d %I64d",&n,&a,&b);
        Matrix m;
        Matrix A=Pow(m,n-2);
        ll ans=(A.mat[0][0]*b+A.mat[0][1]*a+A.mat[0][2]*16+A.mat[0][3]*8+A.mat[0][4]*4+A.mat[0][5]*2+A.mat[0][6]*1)%M;
        cout<<ans<<endl;
    }
}

5952.Counting Cliques(回溯法)

http://acm.hdu.edu.cn/showproblem.php?pid=5952

题目大意:

n个点,m个边,每个点的度不超过20,的无向图,问里面有几个子图是s元完全图?

题目分析:

没什么高明的办法。就是在回溯的基础上加一些pruning。我们用dfs(u,d)表示当前搜索的是点u,已经找到了d个点的完全子图。

用一个数组path记录下搜索路径,当前点的出边数目如果小于s-d,则这个点不可能在解里面,然后搜索的时候只从较小的点往较大的点搜。因为如果1-2-3-4-5是解,那么2-3-4-1-5也是同一个解,所以只从号小往号大的点搜是可以确保不漏解的。

注意因为遍历所有组合,所以搜完一个点回来的时候,要去掉访问标记,因为很显然搜过的点还有可能出现在解里面

建图的方式很巧妙,这个也是参考了博客上大神的题解,先把无向边变成小点指向大点的有向边,再用0-1矩阵存一个完整的图以确保反向边的信息不丢失,然后深搜的时候沿着邻接表搜,判定一个点是否和path中其他点连通的时候用邻接矩阵判断。

#include <bits/stdc++.h>
using namespace std;
#define RE(x) freopen(x,"r",stdin)
#define WR(x) freopen(x,"w",stdout)
typedef long long ll;
int T;
int n,m,s;
vector<int> g[105];
bool graph[105][105];
bool vis[105];
int ans;
int path[105];
void dfs(int u,int d) { //当前搜索u点,已经找到d个点的完全图
    if(d==s) {
        ans++;
        return;
    }
    if(g[u].size()+d<s)
        return;
    for(int i=0;i<g[u].size();i++) {
        int v=g[u][i];
        bool flag=true;
        for(int j=0;j<d;j++) {
            if(!graph[v][path[j]])
                flag=false;
            if(g[v].size()+1+d<s)
                flag=false;
            if(!flag) break;
        }
        if(flag) {
            vis[v]=true;
            path[d]=v;
            dfs(v,d+1);
            vis[v]=false;
        }

    }
}
int main() {

    scanf("%d",&T);
    while(T--) {
        scanf("%d %d %d",&n,&m,&s);
        for(int i=1;i<=n;i++)
            g[i].clear();
        memset(vis,0,sizeof(vis));
        memset(path,0,sizeof(path));
        memset(graph,0,sizeof(graph));
        while(m--) {
            int u,v;
            scanf("%d %d",&u,&v);
            graph[u][v]=graph[v][u]=1;
            g[min(u,v)].push_back(max(u,v));
        }
        ans=0;
        for(int i=1;i<=n;i++) {
            if(!vis[i]) {
                vis[i]=true;
                path[0]=i;
                dfs(i,1);
                vis[i]=false;
            }
        }
        printf("%d\n", ans);
    }
}

5954. Do not pour out(数学,二分答案)

http://acm.hdu.edu.cn/showproblem.php?pid=5952

题目大意:

有一个圆柱形水桶,底面直径是2,高是2,装有h高度的水,现在把杯子倾斜,使得水恰好不溢出,求横截面的面积,保留5位小数。

题目分析:

首先明确本题的截面有两种形状:(图我实在是画不出来,表示matlab不会用的渣渣。。。。)
实在是不会画图了,参考http://blog.csdn.net/danliwoo/article/details/53002695 这篇博文中的图

未授权侵删。

  1. d1 时,是一个椭圆
    如图所示:这里写图片描述

这个椭圆的半长轴等于 (2d)2+1 ,半短轴为1。

  1. d<1 时候,是椭圆切掉一个角
    这里写图片描述

这个就比较复杂了。首先,这个面积就很奇怪,所以我们用他在水平面上的投影(是个弓形)来间接求。

设水平面弓形的圆心角是 2α0 ,面积我们用 S(α0) 表示。则有:

Ssinθ=S(α0)(1)

接下来的问题就是用d表是 θ α0 .

设弓形那条弦的中点到圆柱右下角(相当于图中的(0,2)点)的距离为L,我们知道:

sinθ=LL2+4(2)

α 是锐角和钝角讨论,均有:

L=1cosα0(3)

S(α)=α12sin2α(4)

联立 (1)(2)(3)(4) ,解得:

S=(α012sin2α0)(1cosα0)2+41cosα0(5)

那么接下来我们就要建立d和 α0 的关系.

根据水的体积相等,我们可以列出方程:

πd=20S(y)dy(6)

但是我们只知道 S(α) ,所以还要推出y和 α 的关系。我们发现对任意y,向y轴做垂线,得到的三角形都是相似的,所以我们根据对应边成比例得到:

y=g(α)=2(1cosα)1cosα0(7)

其中 α0 是与积分变量 α 无关的常数。

(7) 代入 (6) ,积分换元得:

πd(1cosα0)2=α00(α12sin2α)sinαdα(8)

积出来,得到:
d=2(sinα0α0cosα013sin3α0)π(1cosα0)(9)

这个关于 α0 的方程是没有解析解的,所以只能求近似的数值解,根据几何意义, α0(d) (0,1) 上单调递增,所以可以二分法求出来,然后代入 (5) 求出S。

注意,这里充分小的eps要取到1e-10及以下,否则会丢精度。

#include <bits/stdc++.h>
using namespace std;
#define eps 1e-10
#define RE(x) freopen(x,"r",stdin)
#define WR(x) freopen(x,"w",stdout)
const double PI = acos(-1.0);
typedef long long ll;
int T;
double d;
double f(double a) {
    return 2*(sin(a)-a*cos(a)-sin(a)*sin(a)*sin(a)/3)/(PI*(1-cos(a)));

}
double getalpha() {
    double l=0,r=PI;
    while(l-r<eps) {
        double mid=(l+r)/2;
        if(fabs(f(mid)-d)<eps)//这里写成1e-9就会错
            return mid;
        else if(f(mid)>d)
            r=mid;
        else
            l=mid;
    }
}
double solve() {
    if(fabs(d)<eps)
        return 0;
    if(fabs(d-1.0)<eps)
        return PI*sqrt(2.0);
    else if((d-eps)>1.0)  //d>1
        return PI*sqrt((2-d)*(2-d)+1.0);
    else { //d<1
        double a=getalpha();
        double s=(a-sin(2*a)/2)*sqrt(4+(1-cos(a))*(1-cos(a)))/((1-cos(a)));
        return s;
    }

}
int main() {
    scanf("%d",&T);
    while(T--) {
        scanf("%lf",&d);
        printf("%.5f\n", solve());
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值