zoukankan      html  css  js  c++  java
  • 2016 ACM-ICPC 区域赛(大连站)题解整理

    题目链接

    A - Wrestling Match (二分图染色)

    题意略坑(没有说好的玩家一定能打过差的玩家啊啊~~)

    典型的二分图染色问题,每个玩家看成一个点,把相互较量过的玩家之间连边,好的玩家染成黑色,差的玩家染成白色。先把能确定颜色的点都确定下来,然后剩下的点判断是不是二分图,推导过程中发现矛盾立即返回No。如果一个点没有和其他任何点相连且颜色不确定也返回No。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 typedef long long ll;
     4 const int N=1000+10;
     5 int hd[N],ne,n,m,X,Y,col[N];
     6 struct E {int v,nxt;} e[20010];
     7 void addedge(int u,int v) {e[ne]= {v,hd[u]},hd[u]=ne++;}
     8 bool dfs(int u,int c) {
     9     if(~col[u])return col[u]==c;
    10     col[u]=c;
    11     for(int i=hd[u]; ~i; i=e[i].nxt) {
    12         int v=e[i].v;
    13         if(!dfs(v,col[u]^1))return 0;
    14     }
    15     return 1;
    16 }
    17 bool solve() {
    18     for(int u=1; u<=n; ++u)if(~col[u]) {
    19             for(int i=hd[u]; ~i; i=e[i].nxt) {
    20                 int v=e[i].v;
    21                 if(!dfs(v,col[u]^1))return 0;
    22             }
    23         }
    24     for(int u=1; u<=n; ++u)if(!~col[u]) {
    25             if(!~hd[u])return 0;
    26             if(!dfs(u,0))return 0;
    27         }
    28     return 1;
    29 }
    30 
    31 int main() {
    32     while(scanf("%d%d%d%d",&n,&m,&X,&Y)==4) {
    33         memset(hd,-1,sizeof hd),ne=0;
    34         memset(col,-1,sizeof col);
    35         while(m--) {
    36             int u,v;
    37             scanf("%d%d",&u,&v);
    38             addedge(u,v);
    39             addedge(v,u);
    40         }
    41         while(X--) {int x; scanf("%d",&x); col[x]=1;}
    42         while(Y--) {int x; scanf("%d",&x); col[x]=0;}
    43         puts(solve()?"YES":"NO");
    44     }
    45     return 0;
    46 }
    View Code

    B - Regular Number (字符串匹配Shift-And算法+bitset优化)

    Shift-And算法,有点抽象,其大致思想是利用前缀移位的方法,对每个数字建立一个bitset,把模板串中所有能出现的位置标上1,然后把原串从头到尾扫一遍,每扫到一个位置,把这个位置加到另一个bitset里,然后和这个位置上的数对应的bitset相与,清除不合法的位置。如果一个位置能够“安全”到达第n-1位,则说明匹配成功,ans++。

    时间卡得很死,要用gets和puts才能过。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 typedef long long ll;
     4 const int N=5e6+10;
     5 int n;
     6 char s[N];
     7 bitset<1000+10> a[10],b;
     8 
     9 int main() {
    10     while(scanf("%d",&n)==1) {
    11         for(int i=0; i<10; ++i)a[i].reset();
    12         b.reset();
    13         for(int i=0; i<n; ++i) {
    14             int x,y;
    15             scanf("%d",&x);
    16             while(x--) {
    17                 scanf("%d",&y);
    18                 a[y].set(i);
    19             }
    20         }
    21         getchar(),gets(s);
    22         for(int i=0; s[i]; ++i) {
    23             b=(b<<1).set(0)&a[s[i]^48];
    24             if(b.test(n-1)) {
    25                 char ch=s[i+1];
    26                 s[i+1]='',puts(s+i-n+1),s[i+1]=ch;
    27             }
    28         }
    29     }
    30     return 0;
    31 }
    View Code

    C - Game of Taking Stones (威佐夫博弈+高精度浮点数)

    威佐夫博弈裸题,(a,b)为必败态当且仅当$a=frac{sqrt{5}+1}{2}(b-a)$。但数据较大,可以用Java中的BigDecimal来做,精度开到小数点后100位以上,二分开根号。

     1 import java.util.*;
     2 import java.io.*;
     3 import java.math.*;
     4 
     5 public class Main {
     6     static BigDecimal eps=new BigDecimal("0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001");
     7     static BigDecimal two=BigDecimal.valueOf(2);
     8     static BigDecimal sqrt(BigDecimal x) {
     9         BigDecimal L=BigDecimal.ZERO,R=x;
    10         while(R.subtract(L).compareTo(eps)>0) {
    11             BigDecimal mid=(L.add(R).divide(two));
    12             BigDecimal p=mid.pow(2);
    13             if(p.compareTo(x)>0)R=mid;
    14             else L=mid;
    15         }
    16         return L;
    17     }
    18     static BigDecimal sqrt5=sqrt(BigDecimal.valueOf(5));
    19     public static void main(String[] args) throws Exception {
    20         Scanner in = new Scanner(System.in);
    21         while(in.hasNext()){
    22         BigDecimal a=in.nextBigDecimal();
    23         BigDecimal b=in.nextBigDecimal();
    24         if(a.compareTo(b)>0) {
    25             BigDecimal t=b;
    26             b=a;
    27             a=t;
    28         }
    29         BigDecimal m=b.subtract(a);
    30         BigDecimal k=m.multiply(BigDecimal.ONE.add(sqrt5)).divide(two);
    31         BigInteger A=a.toBigInteger();
    32         BigInteger B=k.toBigInteger();
    33         if(A.compareTo(B)==0)System.out.println(0);
    34         else System.out.println(1);
    35         }
    36     }
    37 }
    View Code

    D - A Simple Math Problem (数论+一元二次方程)

    设X=k1*c,Y=k2*c,k1与k2互质,则c=gcd(X,Y),b=lcm(X,Y)=XY/gcd(X,Y)=k1*k2*c。

    又有a=X+Y=(k1+k2)*c,由于k1,k2互质,因此(k1+k2)与k1*k2也互质。

    证明:假设(k1+k2)与k1*k2不互质,即含有一个公因数g,则g要么是k1的因子,要么是k2的因子,因此k1,k2中一定有一个能被g整除,那么由于g是(k1+k2)的因子,因此另一个也需要被g整除,与k1,k2互质矛盾。

    因此gcd(a,b)=gcd((k1+k2)*c,k1*k2*c)=c=gcd(X,Y),因此原问题转化成了一个一元二次方程X(a-X)=gcd(a,b),用公式求出两个解,判断是否都为正整数就行了。

    按理说应该还要判断delta是否大于等于0,但不判也能过~~

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 typedef long long ll;
     4 typedef double db;
     5 int a,b;
     6 db A,B,C;
     7 bool isz(db x) {return x==round(x);}
     8 
     9 int main() {
    10     while(scanf("%d%d",&a,&b)==2) {
    11         A=1,B=-a,C=(db)b*__gcd(a,b);
    12         db delta=B*B-4*A*C;
    13         db x=(-B-sqrt(delta))/(2*A);
    14         db y=(-B+sqrt(delta))/(2*A);
    15         if(isz(x)&&isz(y))printf("%.0f %.0f
    ",x,y);
    16         else puts("No Solution");
    17     }
    18     return 0;
    19 }
    View Code

     E - Aninteresting game (二进制规律/树状数组原理)

    通过观察二进制位可以得出:

    第一种询问的答案是[L,R]中所有数的lowbit之和。[1-x]中以某一位为lowbit的数的个数为n在那一位之前的前缀加上那一位对应的数(0或1),从头到尾刷一遍就行了,有点类似数位dp。

    对于第二种询问,一个数x对另一个数y有贡献当且仅当x是y在树状数组中的父节点,用树状数组单点更新的方法即可求出答案。

    对long long类型的1移位一定要加ll后缀,一定要加ll后缀,一定要加ll后缀。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 typedef long long ll;
     4 typedef double db;
     5 ll n,q;
     6 ll lowbit(ll x) {return x&-x;}
     7 ll cal(ll x) {
     8     ll ret=0;
     9     for(ll i=62,j=0; i>=0; j=j<<1|(x>>i&1),--i) {
    10         ret+=(j+(x>>i&1))*(1ll<<i);
    11     }
    12     return ret;
    13 }
    14 ll cal2(ll x) {
    15     ll ret=0;
    16     for(; x<=n; x+=lowbit(x))ret++;
    17     return ret;
    18 }
    19 int main() {
    20     while(scanf("%lld%lld",&n,&q)==2) {
    21         while(q--) {
    22             ll f;
    23             scanf("%lld",&f);
    24             if(f==1) {
    25                 ll l,r;
    26                 scanf("%lld%lld",&l,&r);
    27                 printf("%lld
    ",cal(r)-cal(l-1));
    28             } else if(f==2) {
    29                 ll x;
    30                 scanf("%lld",&x);
    31                 printf("%lld
    ",cal2(x));
    32             }
    33         }
    34     }
    35     return 0;
    36 }
    View Code

    F - Detachment (数学规律)

    通过找规律可以发现,如果n=2+3+4+...的话,乘积最大的拆分方案是从2开始的连续一系列的数。可以求出1-N的所有数的前缀和(不包括1)和前缀积,对每个n二分找答案即可。

    如果有剩余怎么办?把其中一个数变成右端点的数+1即可。

    注意有几个小地方需要特判一下。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 typedef long long ll;
     4 const int N=5e4+10;
     5 const int mod=1e9+7;
     6 int sum[N],mul[N],inv[N],x,ans;
     7 int main() {
     8     sum[1]=0,mul[1]=inv[1]=1;
     9     for(int i=2; i<N; ++i)sum[i]=sum[i-1]+i,mul[i]=(ll)mul[i-1]*i%mod;
    10     for(int i=2; i<N; ++i)inv[i]=(ll)(mod-mod/i)*inv[mod%i]%mod;
    11     int T;
    12     for(scanf("%d",&T); T--;) {
    13         scanf("%d",&x);
    14         if(x==1)ans=1;
    15         else {
    16             int p=upper_bound(sum,sum+N,x)-sum-1;
    17             int y=x-sum[p],z=p+1-y;
    18             ans=mul[p];
    19             if(z==1)ans=(ll)ans*(p+2)%mod*inv[2]%mod;
    20             else if(z>1)ans=(ll)ans*(p+1)%mod*inv[z]%mod;
    21         }
    22         printf("%d
    ",ans);
    23     }
    24     return 0;
    25 }
    View Code

    G - Garden of Eden (树形dp+容斥)

    答案=全部路径数-不包含1个颜色集合的路径数+不包含2个颜色集合的路径数-不包含3个颜色集合的路径数+...,利用容斥原理,枚举所有不包含颜色的集合,用树形dp搞一搞就行了。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 typedef long long ll;
     4 const int N=5e4+10,inf=0x3f3f3f3f;
     5 int n,k,a[N],siz[N],hd[N],ne,ppc[1<<10];
     6 ll ans;
     7 struct E {int v,nxt;} e[N<<1];
     8 void addedge(int u,int v) {e[ne]= {v,hd[u]},hd[u]=ne++;}
     9 void dfs(int u,int fa,int f) {
    10     for(int i=hd[u]; ~i; i=e[i].nxt)if(e[i].v!=fa)dfs(e[i].v,u,f);
    11     if(!siz[u])return;
    12     ans+=f;
    13     for(int i=hd[u]; ~i; i=e[i].nxt)if(e[i].v!=fa) {
    14             int v=e[i].v;
    15             ans+=(ll)siz[v]*siz[u]*2*f;
    16             siz[u]+=siz[v];
    17         }
    18 }
    19 ll solve() {
    20     ans=0;
    21     for(int S=(1<<k)-1; S; --S) {
    22         int f=(k-ppc[S])&1?-1:1;
    23         for(int i=1; i<=n; ++i)siz[i]=S>>a[i]&1;
    24         dfs(1,-1,f);
    25     }
    26     return ans;
    27 }
    28 int main() {
    29     ppc[0]=0;
    30     for(int i=1; i<(1<<10); ++i)ppc[i]=ppc[i>>1]+(i&1);
    31     while(scanf("%d%d",&n,&k)==2) {
    32         memset(hd,-1,sizeof hd),ne=0;
    33         for(int i=1; i<=n; ++i)scanf("%d",&a[i]),a[i]--;
    34         for(int i=1; i<n; ++i) {
    35             int u,v;
    36             scanf("%d%d",&u,&v);
    37             addedge(u,v);
    38             addedge(v,u);
    39         }
    40         printf("%lld
    ",solve());
    41     }
    42     return 0;
    43 }
    View Code

    H - To begin or not to begin (概率)

    从直觉上可以看出,后手不会比先手有利。进一步当球的数量为奇数的时候,先手会比后手多一次取的机会,而偶数的时候先手和后手取球的机会是均等的,因此球的个数为奇数时输出1,偶数输出0。

    1 #include<bits/stdc++.h>
    2 using namespace std;
    3 typedef long long ll;
    4 int n;
    5 
    6 int main() {
    7     while(scanf("%d",&n)==1)printf("%d
    ",n&1^1);
    8     return 0;
    9 }
    View Code

    I - Convex

    签到,会求多边形或者三角形面积即可。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 typedef long long ll;
     4 typedef double db;
     5 const db pi=acos(-1);
     6 db rad,r;
     7 int n;
     8 
     9 int main() {
    10     while(scanf("%d%lf",&n,&r)==2) {
    11         db ans=0;
    12         for(int i=0; i<n; ++i) {
    13             scanf("%lf",&rad),rad*=pi/180;
    14             ans+=sin(rad);
    15         }
    16         printf("%.3f
    ",ans*r*r/2);
    17     }
    18     return 0;
    19 }
    View Code

    J - Find Small A

    签到。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 typedef long long ll;
     4 int n;
     5 
     6 int main() {
     7     while(scanf("%d",&n)==1) {
     8         int x,ans=0;
     9         while(n--)for(scanf("%d",&x); x; x>>=8)if((x&((1<<8)-1))==97)ans++;
    10         printf("%d
    ",ans);
    11     }
    12     return 0;
    13 }
    View Code

    K - Guess the number (dp)

    设dp[n]为区间长度为n时的最小最坏猜测次数,则有状态转移方程$dp[n]=min{max(dp[i],n-i-1)+1},0<=i<n$,直接算的话是$O(n^2)$会T,可以先暴力打个长度为1000的表找一下规律,然后瞎凑凑出答案来。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 typedef long long ll;
     4 const int N=5e6+10,mod=100000073;
     5 int dp[N],siz[N],top[N],bot[N],a,b;
     6 
     7 int main() {
     8     memset(top,-1,sizeof top);
     9     for(int i=0,j=0; i<N-1; i+=++j)top[i+1]=i,dp[i+1]=j+1;
    10     for(int i=0; i<N-1; ++i)if(!~top[i])top[i]=top[i-1];
    11     for(int i=3; i<N; ++i)bot[i]=bot[i-1]+(top[i]==top[i-1]);
    12     siz[0]=1,siz[1]=2,siz[2]=4;
    13     for(int i=3; i<N; ++i)siz[i]=(siz[i-1]+siz[top[i]]-siz[bot[i]-1])%mod;
    14     for(int i=1; i<N; ++i)if(!dp[i])dp[i]=dp[i-1];
    15     while(scanf("%d%d",&a,&b)==2) {
    16         int n=b-a+1;
    17         printf("%d %d
    ",dp[n],((siz[n]-siz[n-1])%mod+mod)%mod);
    18     }
    19     return 0;
    20 }
    View Code

    也可以利用最优区间的单调性将递推复杂度降到$O(n)$

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 typedef unsigned long long ll;
     4 const int N=5e6+10,mod=100000073;
     5 int dp[N],siz[N],a,b;
     6 
     7 int main() {
     8     dp[0]=0,siz[0]=1;
     9     for(int i=1,hd=0,tl=1,sum=1; i<N; ++i) {
    10         for(; tl<i&&max(dp[tl],i-tl-1)<=max(dp[tl-1],i-(tl-1)-1); sum=(sum+siz[tl++])%mod);
    11         for(; hd<tl&&max(dp[hd],i-hd-1)!=max(dp[tl-1],i-(tl-1)-1); sum=(sum-siz[hd++]+mod)%mod);
    12         dp[i]=max(dp[hd],i-hd-1)+1,siz[i]=sum;
    13     }
    14     while(scanf("%d%d",&a,&b)==2)printf("%d %d
    ",dp[b-a+1],siz[b-a+1]);
    15     return 0;
    16 }
    View Code
  • 相关阅读:
    Step by step Dynamics CRM 2013安装
    SQL Server 2012 Managed Service Account
    Step by step SQL Server 2012的安装
    Step by step 活动目录中添加一个子域
    Step by step 如何创建一个新森林
    向活动目录中添加一个子域
    活动目录的信任关系
    RAID 概述
    DNS 正向查找与反向查找
    Microsoft Dynamics CRM 2013 and 2011 Update Rollups and Service Packs
  • 原文地址:https://www.cnblogs.com/asdfsag/p/10579405.html
Copyright © 2011-2022 走看看