2016 acm/icpc 大连站现场赛(7道题)

5971.Wrestling Match(并查集,二分染色)

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

题目大意:

(这道题的题意有点说的不太明白,我大体理解是这样的:)

有n个玩家,m场比赛,其中有x个人是good,y个人是bad,每一场比赛都看做是一个good和一个bad的比赛,问是否所有人都能分成good和bad。

题目分析:

其实这题我一直有个疑问,就是关于“不确定”的情况,如果是一个正方形ABCD,那么按测试用例就是可以分的,但按我的理解,只能确定AC和BD分为两组,但谁是good谁是bad并不知道。不过既然测试用例是这样的,也经历了若干发wa,这题应该只能这么理解:

首先这个图有可能是断的,那么如果有1个点的分量,若它是不知道的,那就不能确定,否则,则看这个分量在部分点已经确定颜色的情况下能否二分染色即可

不过我还是吃了一大记wa,因为颜色数组不能当visited数组 有颜色的点不一定访问过,这样在搜的时候有可能会停下来。

#include 
using namespace std;
#define RE(x) freopen(x,"r",stdin)
#define WR(x) freopen(x,"w",stdout)
typedef long long ll;
int n,m,x,y;
int u,v;
vector<int> g[1005];
int c[1005];
int father[1005],num[1005],id[1005];
bool vis[1005];
bool dfs(int u,int col) { //给u点染col
    c[u]=col;
    vis[u]=true;
    for(int i=0;iint v=g[u][i];
        if(c[v]==col)
            return false;
        else if(!vis[v]) {
            if(col==1) {
                if(!dfs(v,2)) return false;
            }
            else {
                if(!dfs(v,1)) return false;
            }
        }
    }
    return true;
}
int find(int x) {
    return father[x]==x?x:find(father[x]);
}
void Union(int i,int j) {
    i=find(i);
    j=find(j);
    if(i!=j) {
        father[i]=j;
        num[j]+=num[i];
    }
}
int main() {
    while(~scanf("%d %d %d %d",&n,&m,&x,&y)) {
        for(int i=1;i<=n;i++) {
            g[i].clear();
        }
        memset(c,0,sizeof(c));
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=n;i++)
        {
            father[i]=i;
            num[i]=1;/*开始时数量都为1,根节点为自己*/
            id[i]=0;
        }
        for(int i=0;iscanf("%d %d",&u,&v);
            g[u].push_back(v);
            g[v].push_back(u);
            Union(u,v);
        }
        for(int i=0;iscanf("%d",&u);
            c[u]=1;
            id[find(u)]=u;
        }
        for(int i=0;iscanf("%d",&u);
            c[u]=2;
            id[find(u)]=u;
        }
        bool ans=true;
        if(n==1 && c[1]==0)
            ans=false;
        for(int i=1;i<=n;i++) {
            if(num[find(i)]==1 && c[i]==0) {
                ans=false;
                break;
            }
        }
        if(ans) {
            for(int i=1;i<=n;i++) {
                int p=find(i);//得到i点的祖先
                if(id[p]==0) { //这一坨都没有染过色
                    ans=dfs(p,1);
                }
                else { //id[p]表示有染色的点
                    ans=dfs(id[p],c[id[p]]);
                }
                if(!ans)
                    break;
            }
        }
        if(ans)
            printf("YES\n");
        else
            printf("NO\n");
    }
}

5973.Game of Taking Stones(模板题)

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

题目大意:

有两堆石头子,一堆n个一堆m个,两人做游戏,每次可以拿一堆的任意多个,或两堆同时拿相同多个,拿到最后一个的获胜,求同样采取最优策略下,先手胜还是后手胜?

题目分析:

这个叫威佐夫博弈,后手胜当且仅当
n=(mn)1+52 .

用到大数还是写java吧。。。。这题可以拿去做模板.


import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Scanner;


public class Main {
    static BigDecimal gold = new BigDecimal("1.61803398874989484820458683436563811772030917980576286213544862270526046281890244970720720418939113748475408807538689175212663386222353693179318006076672635443338908659593958290563832266131992829026788");

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        while(sc.hasNext()) {
            BigDecimal a = new BigDecimal(sc.next());
            BigDecimal b = new BigDecimal(sc.next());
            if(a.compareTo(b)==1) {
                BigDecimal c=a;
                a=b;
                b=c;
            }
            BigDecimal temp = b.subtract(a).multiply(gold).subtract(a);
            //if(a!=floor(k*gold)

            if(temp.compareTo(BigDecimal.ONE)==-1 && temp.compareTo(BigDecimal.ZERO)==1)
                System.out.println(0);
            else
                System.out.println(1);

        }
    }

}

5974.A Simple Math Problem(数学)

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

题目大意:

求X和Y,满足 X+Y=a,lcm(x,y)=b .

题目分析:

表面上看挺简单的,a才20000,枚举a求lcm就得了,但那就完蛋了,因为测试用例有12万个。

遇到lcm还是要往gcd上去想。设 gcd(x,y)=c,x=ic,y=jc ,则原式化为:

{ic+jc=aijc=b

所以i,j是方程 cx2ax+b=0 的两个根。又由于i+j和ij互质,所以gcd(a,b)=c.那么解那个方程就好了。

#include 
using namespace std;
#define RE(x) freopen(x,"r",stdin)
#define WR(x) freopen(x,"w",stdout)
typedef long long ll;
ll gcd(ll a, ll b)
{
    if(b==0)
        return a;
    return gcd(b,a%b);
}
ll a,b;
int T;
int main() {

    while(~scanf("%I64d %I64d",&a,&b)) {
        ll c=gcd(a,b);
        ll dt=a*a-4*b*c;
        if(dt<0) {
            cout<<"No Solution"<continue;
        }
        else {
            ll sq=sqrt(dt);
            if(sq*sq!=dt) {
                cout<<"No Solution"<continue;
            }
            else if((a+sq)%(2*c)){
                cout<<"No Solution"<continue;
            }
            else {
                ll x1=(a-sq)/2;
                ll x2=(a+sq)/2;
                cout<' '<

5976.Detachment(数学题,二分答案)

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

题目大意:

输入数字x,把x拆成若干不相同的数字,使得乘积最大。

题目分析;

Leetcode第343题的变形。原题题解我写过:http://blog.csdn.net/cmershen/article/details/51548825

原题的基础上加了不相同的要求,那么就不可能拆出尽可能多的3或2了,而只能是2,3,4,5…这么拆.

那么首先二分k,使得 2+..+kx<2+...+k+k+1 .那么就存在一个 s[0,k] ,使得 2+...+k+s=x .

接下来讨论s:
* s=0: 答案就是 k!
* s=k: 把2和s合并为k+2
* 其他: 把s和k+1-s合并为k+1

因为数据很大,先预处理出k!%M和k的乘法逆元。算了一下x的范围是 109 ,k最多取到44721.这个计算量和空间完全可以接受。(而且二分答案个人认为可以看做是O(1)复杂度的,因为答案范围终归是有限的,与输入的n无关)

#include 
using namespace std;
#define RE(x) freopen(x,"r",stdin)
#define WR(x) freopen(x,"w",stdout)
typedef long long ll;
const int M = 1e9+7;
ll a[44800],b[44800],c[44800];
int T;
ll n;
int egcd(int a, int b, int &x, int &y) {
  if (!b) {
    x = 1;
    y = 0;
    return a;
  }
  int ans = egcd(b, a % b, x, y);
  int temp = x;
  x = y;
  y = temp - a / b * y;
  return ans;
}
int cal(int a, int m = M) { //扩展欧几里得算法模板,求a关于M的乘法逆元
  int x, y;
  int gcd = egcd(a, m, x, y);
  if (1 % gcd != 0)
    return -1;
  x *= 1 / gcd;
  m = abs(m);
  int ans = x % m;
  if (ans <= 0)
    ans += m;
  return ans;
}

void pre() {
    b[0]=1;
    for(int i=1;i<=44723;i++) {
        a[i]=i*(i+1)/2-1;
        b[i]=b[i-1]*i%M;
        c[i]=cal(i);
    }
}
int find(ll x) {
    int l=1,r=44721;
    while(l//cout<","<int mid=(l+r)/2;
        if(a[mid]<=x && x1])
            return mid;
        else if(a[mid]>x)
            r=mid;
        else
            l=mid+1;
    }
    return -1;
}
int main() {
    pre();

    scanf("%d",&T);
    while (T--) {
        scanf("%I64d",&n);
        if(n<=4) {
            printf("%lld\n",n);
            continue;
        }
        int k=find(n);
        int s=n-a[k];
        ll ans=0;
        if(s==0) {
            ans=b[k];
        }
        else if(s==k) {
            ans=b[k]*c[2]%M;
            ans=ans*(k+2)%M;
        }
        else {
            ans=b[k]*c[k+1-s]%M;
            ans=ans*(k+1)%M;
        }
        printf("%lld\n", ans);

    }
    return 0;
}

5978. To begin or not to begin(找规律,数学归纳法)

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

题目大意:

一个暗箱里有1个红球和n个黑球,两人轮流不放回的取,谁先取到红球谁赢。问先手方和后手方谁优?或是公平?

题目分析:

先写递推公式:

如果n=0,则先手方必胜。 P0=1.

如果n=1,则是公平的。 P1=12.

对任意n,先手方第一步有 1/n 的机会一步获胜,如果不能获胜,还需要对方败,那么得出递推式:

Pn=1n+n1n(1Pn1) .

写几项发现如果n为奇数则 Pn=1/2 ,否则 Pn>1/2 .如果是比赛,直接那么写过去就过了。

不过学习的过程是不允许一知半解的。

接下来我们用数学归纳法证明这个结论。

首先 P0=1>1/2,P1=1/2 .假设对 kN+ ,有 P2k>1/2,P2k+1=1/2 .

P2k+2=12k+2+2k+12k+212=2k+34k+4=12+14k+4>12.

P2k+3=12k+3+2k+22k+3(12k+34k+4)=12.

#include 
using namespace std;
typedef long long ll;
int k;
int main(){
    while (~scanf("%d",&k)) {
        if(k==1)
            printf("0\n");
        else
            printf("1\n");
    }
}

5979.Convex(签到题)

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

题目大意:

有个凸多边形,每个点距离原点的距离都是d,相邻两个点和原点连线的夹角给出,求面积

题目分析:

三角形面积公式,S=absinC/2,注意这里面给的是角度不是弧度。

#include 
using namespace std;
typedef long long ll;
#define INF 0x3f3f3f3f
const double PI = acos(-1.0);
const int M = 1e9+7;
#define RE(x) freopen(x,"r",stdin)
#define WR(x) freopen(x,"w",stdout)
int n,d,a;
int main() {
    //RE("in.txt");WR("out.txt");
    while(~scanf("%d %d",&n,&d)) {
        double ans=0;
        for(int i=0;iscanf("%d",&a);
            ans+=d*d*sin(a*1.0/180*PI)/2;
        }
        printf("%.3f\n",ans);
    }
}

5980.Find Small A(签到题)

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

题目大意:

给你若干个32位无符号数,每个数分别表示为4个ascii码,问ascii码对应了几个’a’.

题目分析:

太水了,就是求每个数的256进制表示,有几位是97。

#include 
using namespace std;
typedef long long ll;
#define INF 0x3f3f3f3f
const double PI = acos(-1.0);
const int M = 1e9+7;
#define RE(x) freopen(x,"r",stdin)
#define WR(x) freopen(x,"w",stdout)
int T;
ll a;
int main() {

    scanf("%d",&T);
    int cnt=0;
    for(int t=1;t<=T;t++) {
        scanf("%lld",&a);
        while(a) {
            if(a%256==97)
                cnt++;
            a/=256;
        }
    }
    printf("%d\n",cnt);
}

据说这是个7题铜牌的时代,果然以我的理解力只能做出这7道了。还有道树状数组的题,和树分治,分分钟表示一脸懵逼.

你可能感兴趣的:(another,oj)