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
  • 相关阅读:
    12
    Kafka设计解析(二)- Kafka High Availability (上)
    Apache kafka 工作原理介绍
    Kafka设计解析(一)- Kafka背景及架构介绍
    【转载】MySQL之权限管理
    【转载】漫谈HADOOP HDFS BALANCER
    【转载】HDFS 上传文件不均衡和Balancer太慢的问题
    【转载】mysql binlog日志自动清理及手动删除
    【转】Typora极简教程
    更新Linux服务器时间
  • 原文地址:https://www.cnblogs.com/asdfsag/p/10579405.html
Copyright © 2011-2022 走看看