2016 ACM-ICPC 区域赛(大连站)题解
A - Wrestling Match (二分图染色)
题意略坑(没有说好的玩家一定能打过差的玩家啊啊~~)
典型的二分图染色问题,每个玩家看成一个点,把相互较量过的玩家之间连边,好的玩家染成黑色,差的玩家染成白色。先把能确定颜色的点都确定下来,然后剩下的点判断是不是二分图,推导过程中发现矛盾立即返回No。如果一个点没有和其他任何点相连且颜色不确定也返回No。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=+;
int hd[N],ne,n,m,X,Y,col[N];
struct E {int v,nxt;} e[];
void addedge(int u,int v) {e[ne]= {v,hd[u]},hd[u]=ne++;}
bool dfs(int u,int c) {
if(~col[u])return col[u]==c;
col[u]=c;
for(int i=hd[u]; ~i; i=e[i].nxt) {
int v=e[i].v;
if(!dfs(v,col[u]^))return ;
}
return ;
}
bool solve() {
for(int u=; u<=n; ++u)if(~col[u]) {
for(int i=hd[u]; ~i; i=e[i].nxt) {
int v=e[i].v;
if(!dfs(v,col[u]^))return ;
}
}
for(int u=; u<=n; ++u)if(!~col[u]) {
if(!~hd[u])return ;
if(!dfs(u,))return ;
}
return ;
} int main() {
while(scanf("%d%d%d%d",&n,&m,&X,&Y)==) {
memset(hd,-,sizeof hd),ne=;
memset(col,-,sizeof col);
while(m--) {
int u,v;
scanf("%d%d",&u,&v);
addedge(u,v);
addedge(v,u);
}
while(X--) {int x; scanf("%d",&x); col[x]=;}
while(Y--) {int x; scanf("%d",&x); col[x]=;}
puts(solve()?"YES":"NO");
}
return ;
}
B - Regular Number (字符串匹配Shift-And算法+bitset优化)
Shift-And算法,有点抽象,其大致思想是利用前缀移位的方法,对每个数字建立一个bitset,把模板串中所有能出现的位置标上1,然后把原串从头到尾扫一遍,每扫到一个位置,把这个位置加到另一个bitset里,然后和这个位置上的数对应的bitset相与,清除不合法的位置。如果一个位置能够“安全”到达第n-1位,则说明匹配成功,ans++。
时间卡得很死,要用gets和puts才能过。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5e6+;
int n;
char s[N];
bitset<+> a[],b; int main() {
while(scanf("%d",&n)==) {
for(int i=; i<; ++i)a[i].reset();
b.reset();
for(int i=; i<n; ++i) {
int x,y;
scanf("%d",&x);
while(x--) {
scanf("%d",&y);
a[y].set(i);
}
}
getchar(),gets(s);
for(int i=; s[i]; ++i) {
b=(b<<).set()&a[s[i]^];
if(b.test(n-)) {
char ch=s[i+];
s[i+]='\0',puts(s+i-n+),s[i+]=ch;
}
}
}
return ;
}
C - Game of Taking Stones (威佐夫博弈+高精度浮点数)
威佐夫博弈裸题,(a,b)为必败态当且仅当$a=\frac{\sqrt{5}+1}{2}(b-a)$。但数据较大,可以用Java中的BigDecimal来做,精度开到小数点后100位以上,二分开根号。
import java.util.*;
import java.io.*;
import java.math.*; public class Main {
static BigDecimal eps=new BigDecimal("0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001");
static BigDecimal two=BigDecimal.valueOf(2);
static BigDecimal sqrt(BigDecimal x) {
BigDecimal L=BigDecimal.ZERO,R=x;
while(R.subtract(L).compareTo(eps)>0) {
BigDecimal mid=(L.add(R).divide(two));
BigDecimal p=mid.pow(2);
if(p.compareTo(x)>0)R=mid;
else L=mid;
}
return L;
}
static BigDecimal sqrt5=sqrt(BigDecimal.valueOf(5));
public static void main(String[] args) throws Exception {
Scanner in = new Scanner(System.in);
while(in.hasNext()){
BigDecimal a=in.nextBigDecimal();
BigDecimal b=in.nextBigDecimal();
if(a.compareTo(b)>0) {
BigDecimal t=b;
b=a;
a=t;
}
BigDecimal m=b.subtract(a);
BigDecimal k=m.multiply(BigDecimal.ONE.add(sqrt5)).divide(two);
BigInteger A=a.toBigInteger();
BigInteger B=k.toBigInteger();
if(A.compareTo(B)==0)System.out.println(0);
else System.out.println(1);
}
}
}
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,但不判也能过~~
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
int a,b;
db A,B,C;
bool isz(db x) {return x==round(x);} int main() {
while(scanf("%d%d",&a,&b)==) {
A=,B=-a,C=(db)b*__gcd(a,b);
db delta=B*B-*A*C;
db x=(-B-sqrt(delta))/(*A);
db y=(-B+sqrt(delta))/(*A);
if(isz(x)&&isz(y))printf("%.0f %.0f\n",x,y);
else puts("No Solution");
}
return ;
}
E - Aninteresting game (二进制规律/树状数组原理)
通过观察二进制位可以得出:
第一种询问的答案是[L,R]中所有数的lowbit之和。[1-x]中以某一位为lowbit的数的个数为n在那一位之前的前缀加上那一位对应的数(0或1),从头到尾刷一遍就行了,有点类似数位dp。
对于第二种询问,一个数x对另一个数y有贡献当且仅当x是y在树状数组中的父节点,用树状数组单点更新的方法即可求出答案。
对long long类型的1移位一定要加ll后缀,一定要加ll后缀,一定要加ll后缀。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
ll n,q;
ll lowbit(ll x) {return x&-x;}
ll cal(ll x) {
ll ret=;
for(ll i=,j=; i>=; j=j<<|(x>>i&),--i) {
ret+=(j+(x>>i&))*(1ll<<i);
}
return ret;
}
ll cal2(ll x) {
ll ret=;
for(; x<=n; x+=lowbit(x))ret++;
return ret;
}
int main() {
while(scanf("%lld%lld",&n,&q)==) {
while(q--) {
ll f;
scanf("%lld",&f);
if(f==) {
ll l,r;
scanf("%lld%lld",&l,&r);
printf("%lld\n",cal(r)-cal(l-));
} else if(f==) {
ll x;
scanf("%lld",&x);
printf("%lld\n",cal2(x));
}
}
}
return ;
}
F - Detachment (数学规律)
通过找规律可以发现,如果n=2+3+4+...的话,乘积最大的拆分方案是从2开始的连续一系列的数。可以求出1-N的所有数的前缀和(不包括1)和前缀积,对每个n二分找答案即可。
如果有剩余怎么办?把其中一个数变成右端点的数+1即可。
注意有几个小地方需要特判一下。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5e4+;
const int mod=1e9+;
int sum[N],mul[N],inv[N],x,ans;
int main() {
sum[]=,mul[]=inv[]=;
for(int i=; i<N; ++i)sum[i]=sum[i-]+i,mul[i]=(ll)mul[i-]*i%mod;
for(int i=; i<N; ++i)inv[i]=(ll)(mod-mod/i)*inv[mod%i]%mod;
int T;
for(scanf("%d",&T); T--;) {
scanf("%d",&x);
if(x==)ans=;
else {
int p=upper_bound(sum,sum+N,x)-sum-;
int y=x-sum[p],z=p+-y;
ans=mul[p];
if(z==)ans=(ll)ans*(p+)%mod*inv[]%mod;
else if(z>)ans=(ll)ans*(p+)%mod*inv[z]%mod;
}
printf("%d\n",ans);
}
return ;
}
G - Garden of Eden (树形dp+容斥)
答案=全部路径数-不包含1个颜色集合的路径数+不包含2个颜色集合的路径数-不包含3个颜色集合的路径数+...,利用容斥原理,枚举所有不包含颜色的集合,用树形dp搞一搞就行了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5e4+,inf=0x3f3f3f3f;
int n,k,a[N],siz[N],hd[N],ne,ppc[<<];
ll ans;
struct E {int v,nxt;} e[N<<];
void addedge(int u,int v) {e[ne]= {v,hd[u]},hd[u]=ne++;}
void dfs(int u,int fa,int f) {
for(int i=hd[u]; ~i; i=e[i].nxt)if(e[i].v!=fa)dfs(e[i].v,u,f);
if(!siz[u])return;
ans+=f;
for(int i=hd[u]; ~i; i=e[i].nxt)if(e[i].v!=fa) {
int v=e[i].v;
ans+=(ll)siz[v]*siz[u]**f;
siz[u]+=siz[v];
}
}
ll solve() {
ans=;
for(int S=(<<k)-; S; --S) {
int f=(k-ppc[S])&?-:;
for(int i=; i<=n; ++i)siz[i]=S>>a[i]&;
dfs(,-,f);
}
return ans;
}
int main() {
ppc[]=;
for(int i=; i<(<<); ++i)ppc[i]=ppc[i>>]+(i&);
while(scanf("%d%d",&n,&k)==) {
memset(hd,-,sizeof hd),ne=;
for(int i=; i<=n; ++i)scanf("%d",&a[i]),a[i]--;
for(int i=; i<n; ++i) {
int u,v;
scanf("%d%d",&u,&v);
addedge(u,v);
addedge(v,u);
}
printf("%lld\n",solve());
}
return ;
}
H - To begin or not to begin (概率)
从直觉上可以看出,后手不会比先手有利。进一步当球的数量为奇数的时候,先手会比后手多一次取的机会,而偶数的时候先手和后手取球的机会是均等的,因此球的个数为奇数时输出1,偶数输出0。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n; int main() {
while(scanf("%d",&n)==)printf("%d\n",n&^);
return ;
}
I - Convex
签到,会求多边形或者三角形面积即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const db pi=acos(-);
db rad,r;
int n; int main() {
while(scanf("%d%lf",&n,&r)==) {
db ans=;
for(int i=; i<n; ++i) {
scanf("%lf",&rad),rad*=pi/;
ans+=sin(rad);
}
printf("%.3f\n",ans*r*r/);
}
return ;
}
J - Find Small A
签到。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n; int main() {
while(scanf("%d",&n)==) {
int x,ans=;
while(n--)for(scanf("%d",&x); x; x>>=)if((x&((<<)-))==)ans++;
printf("%d\n",ans);
}
return ;
}
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的表找一下规律,然后瞎凑凑出答案来。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5e6+,mod=;
int dp[N],siz[N],top[N],bot[N],a,b; int main() {
memset(top,-,sizeof top);
for(int i=,j=; i<N-; i+=++j)top[i+]=i,dp[i+]=j+;
for(int i=; i<N-; ++i)if(!~top[i])top[i]=top[i-];
for(int i=; i<N; ++i)bot[i]=bot[i-]+(top[i]==top[i-]);
siz[]=,siz[]=,siz[]=;
for(int i=; i<N; ++i)siz[i]=(siz[i-]+siz[top[i]]-siz[bot[i]-])%mod;
for(int i=; i<N; ++i)if(!dp[i])dp[i]=dp[i-];
while(scanf("%d%d",&a,&b)==) {
int n=b-a+;
printf("%d %d\n",dp[n],((siz[n]-siz[n-])%mod+mod)%mod);
}
return ;
}
也可以利用最优区间的单调性将递推复杂度降到$O(n)$
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ll;
const int N=5e6+,mod=;
int dp[N],siz[N],a,b; int main() {
dp[]=,siz[]=;
for(int i=,hd=,tl=,sum=; i<N; ++i) {
for(; tl<i&&max(dp[tl],i-tl-)<=max(dp[tl-],i-(tl-)-); sum=(sum+siz[tl++])%mod);
for(; hd<tl&&max(dp[hd],i-hd-)!=max(dp[tl-],i-(tl-)-); sum=(sum-siz[hd++]+mod)%mod);
dp[i]=max(dp[hd],i-hd-)+,siz[i]=sum;
}
while(scanf("%d%d",&a,&b)==)printf("%d %d\n",dp[b-a+],siz[b-a+]);
return ;
}
2016 ACM-ICPC 区域赛(大连站)题解的更多相关文章
- 2016 ACM/ICPC 区域赛(北京) E 题 bfs
https://vjudge.net/problem/UVALive-7672 题意 输入一个五位数n 问由12345变到n的操作最少次数 不可达输出-1 有三种操作 1.交换相邻的位置 次数不 ...
- 2019年icpc区域赛银川站总结
目录 一.前言 二.10月19日热身赛 三.10月20日正式赛 四.结果 一.前言 比赛前我们队有ccpc厦门和icpc银川的名额,然而这两个地区的时间正好撞了,考虑到银川更容易拿奖,加上我们ACM协 ...
- 2014年acm亚洲区域赛·鞍山站
今天北京赛站的比赛也结束了···看了一天的直播之后意识到鞍山站的比赛都过去了一个多月了···这一个月比较萎靡···整天都在睡觉写报告画工图中度过··· 鞍山比哈尔滨还是暖和很多的···就是山上有奇怪的 ...
- 近几年ACM/ICPC区域赛铜牌题
2013 changsha zoj 3726 3728 3736 3735 2013 chengdu hud 4786 4788 4790 2013 hangzhou hdu 4770 4771 47 ...
- 2018 ACM-ICPC 区域赛(青岛站)题解整理
题目链接 C - Flippy Sequence(组合数学+分类讨论) 两区间异或一下,分段考虑,如果全为0则任选两相同区间,答案为$C_{n+1}^{2}=\frac{n(n+1)}{2}$,只有一 ...
- 2016 ACM/ICPC亚洲区青岛站现场赛(部分题解)
摘要 本文主要列举并求解了2016 ACM/ICPC亚洲区青岛站现场赛的部分真题,着重介绍了各个题目的解题思路,结合详细的AC代码,意在熟悉青岛赛区的出题策略,以备战2018青岛站现场赛. HDU 5 ...
- 2016 ACM/ICPC Asia Regional Qingdao Online(2016ACM青岛网络赛部分题解)
2016 ACM/ICPC Asia Regional Qingdao Online(部分题解) 5878---I Count Two Three http://acm.hdu.edu.cn/show ...
- 2016 ACM/ICPC Asia Regional Shenyang Online 1003/HDU 5894 数学/组合数/逆元
hannnnah_j’s Biological Test Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/131072 K ...
- 2016 ACM/ICPC Asia Regional Qingdao Online 1001/HDU5878 打表二分
I Count Two Three Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others ...
随机推荐
- python常用模块——logger模块
python的logging模块提供了通用的日志系统,熟练使用logging模块可以方便开发者开发第三方模块或者是自己的python应用. python使用logging模块记录日志涉及四个主要的类: ...
- Web Deploy 安装及问题解决
注意: 站点名称: 服务器上IIS的站点名称. . 我之前这里随便写一直不成功. 返回500..... 用户名, 密码: 这里最好用windows帐号. 问题比较少. 目标URL: 可不写. 可 ...
- LeetCode:搜索旋转排序数组【33】
LeetCode:搜索旋转排序数组[33] 题目描述 假设按照升序排序的数组在预先未知的某个点上进行了旋转. ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] ...
- 一步步讲解如何开源自己的项目到GitHub上,Mac机示例
如果你有自己的优秀项目,想要分享给大家,那GitHub会是你正确的选择.如何才能将自己的项目上传到GitHub上呢?接下来请一步一步跟着走. 需要准备的资源: 1.一台Mac机 2.安装git客户端( ...
- python中类(class)和实例(instance)
面向对象最重要的概念就是类(Class)和实例(Instance),必须牢记类是抽象的模板,比如Student类,而实例是根据类创建出来的一个个具体的“对象”,每个对象都拥有相同的方法,但各自的数据可 ...
- php数组函数-array_reduce()
array_reduce()函数发送数组中的值到用户自定义函数,并返回一个字符串. 注:如果数组是空的或则初始化值未传递,该函数返回NULL array_reduce(array,myfunction ...
- [转载]Runtime详解
Runtime的特性主要是消息(方法)传递,如果消息(方法)在对象中找不到,就进行转发,具体怎么实现的呢.我们从下面几个方面探寻Runtime的实现机制. Runtime介绍 Runtime消息传 ...
- 【P1714】切蛋糕(单调队列)
实在不明白难度等级,难不成前缀和是个很变态的东西? 说白了就是单调队列裸题,都没加什么别的东西,就是一个前缀和的计算,然而这个题也不是要用它优化,而是必须这么做啊. #include<iostr ...
- Javascript中的prototype和__proto__的联系区别
转载至http://www.cnblogs.com/sinstone/p/5136871.html 一.联系 prototype和__proto__都指向原型对象,任意一个函数(包括构造函数)都有 ...
- Delphi调用Java类
1. Delphi XE7调用Java Class,JAR http://www.th7.cn/Program/delphi/201409/277888.shtml ZC: 文章中又提到:http:/ ...