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]='\0',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\n",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\n",cal(r)-cal(l-1));
28             } else if(f==2) {
29                 ll x;
30                 scanf("%lld",&x);
31                 printf("%lld\n",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\n",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\n",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",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\n",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\n",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\n",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\n",dp[b-a+1],siz[b-a+1]);
15     return 0;
16 }
View Code

 

posted @ 2019-03-22 18:48  jrltx  阅读(1019)  评论(0编辑  收藏  举报