您的位置:首页 > 其它

2016ACM/ICPC亚洲区大连站【solved:10 / 11】

2017-10-06 15:39 501 查看

hdu 5971 Wrestling Match(二分图染色)

题意: 给出 n 个人,m 场 比赛 x 个已经确定的好人 y 个已经确定的坏人。每场比赛都是好人和坏人对决。问是否能够将每个人划分成好人或者坏人。

思路:建图以后,把已知的好人坏人跑一遍dfs,染色染下去,最后对剩下的尚未染色的进行二分图染色即可。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1000 + 5;
vector<int>G[maxn];
vector<int>good, bad;
int ok;
int col[maxn];
void dfs(int cur, int pa)
{
for(auto v : G[cur])    if(v != pa)
{
if(col[v] == col[cur])  ok = 0;
else if(col[v] == 0)
{
col[v] = 3 - col[cur];
dfs(v, cur);
}
}
}
bool bipartite(int cur)
{
for(auto o : G[cur])
{
if(col[o] == col[cur])
{
ok = 0;
return false;
}
if(col[o] == 0)
{
col[o] = 3 - col[cur];
if(!bipartite(o))
{
ok = 0;
return false;
}
}
}
return true;
}

int main()
{
int n, m, x, y;
while(~scanf("%d%d%d%d", &n, &m, &x, &y))
{
for(int i = 1; i <= n; i++) G[i].clear(), col[i] = 0;
good.clear
4000
(), bad.clear();
for(int i = 0; i < m; i++)
{
int u, v;
scanf("%d%d", &u, &v);
G[u].push_back(v);
G[v].push_back(u);
}
for(int i = 0; i < x; i++)
{
int u;
scanf("%d", &u);
good.push_back(u);
}
for(int i = 0; i < y; i++)
{
int u;
scanf("%d", &u);
bad.push_back(u);
}

ok = 1;
for(auto o : good)
{
col[o] = 1;
dfs(o, -1);
}
for(auto o : bad)
{
if(col[o] == 0) col[o] = 2;
else if(col[o] == 1)    ok = 0;
dfs(o, -1);
}

for(int i = 1; i <= n; i++)
{
if(G[i].size() == 0)    continue;
if(col[i] == 0)
{
col[i] = 1;
bipartite(i);
}
}

for(int i = 1; i <= n; i++)
{
if(col[i] == 0) ok = 0;
}
if(ok)  puts("YES");
else puts("NO");
}
return 0;
}


hdu 5972 Regular Number(类dp(所谓shift-and?)

题意:给你N位数,接下来有N行,第i行先输入n,表示这个数的第i 位上可以在接下来的n个数中挑选,然后i 行再输n个数。然后输入需要匹配的母串S,让你输出母串中有多少个可行的N位子串。(1≤N≤1000),(|S|≤5e6)

思路:emmm我没注意是不是shift-and。感觉就是一种类似dp的思想啊,维持dp[len],长度为len的是否合法。然后每次检测新的长度的时候只需要把dp[len] &= dp[len - 1]即可。然后如果dp[n-1]==1,说明有个解了,更新一下答案即可。最后是利用bitset优化整个过程。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 5e6 + 5;
bitset<1005>num[10];
bitset<1005>ans;
char s[maxn];
int main()
{
int n;
while(~scanf("%d", &n))
{
for(int i = 0; i < 10; i++) num[i].reset();
ans.reset();
for(int i = 0; i < n; i++)
{
int a;
scanf("%d", &a);
while(a--)
{
int x;
scanf("%d", &x);
num[x].set(i);
}
}
getchar();
gets(s);
int len = strlen(s);
for(int i = 0; i < len; i++)
{
int temp = s[i] - '0';
ans = ans << 1;
ans[0] = 1;
ans &= num[temp];
if(ans[n-1])
{
char ch = s[i + 1];
s[i+1] = '\0';
puts(s+i-n+1);
s[i+1] = ch;
}
}
}
return 0;
}


hdu 5973 Game of Taking Stones(威佐夫博弈模板题+高精度)

题意:威佐夫博弈模板题,输入数据10100。

思路:拿java写高精度。emmm第一次写比较emmmm。很多点要注意啊。

import java.util.*;
import java.math.*;
public class Main
{
public static void main(String[] args)
{
BigDecimal one = new BigDecimal(1);
BigDecimal two = new BigDecimal(2);
BigDecimal five = new BigDecimal(5);
BigDecimal lb = new BigDecimal(2);
BigDecimal rb = new BigDecimal(3);

for(int i = 0; i < 1000; i++)
{
BigDecimal mid = lb.add(rb).divide(two);
if(mid.multiply(mid).compareTo(five) < 0)    lb = mid;
else rb = mid;
}
BigDecimal goldCut = lb.add(one).divide(two);
BigDecimal a, b;
Scanner input = new Scanner(System.in);

while(input.hasNext())
{
a = input.nextBigDecimal();
b = input.nextBigDecimal();
if(a.compareTo(b) > 0)
{
BigDecimal temp = a;
a = b;
b = temp;
}

b = b.subtract(a).multiply(goldCut);
b = b.setScale(0, BigDecimal.ROUND_DOWN);
if(a.compareTo(b) == 0)    System.out.println(0);
else System.out.println(1);
}
}
}


hdu 5974 A Simple Math Problem(简单数论)

题意:给你a和b,找到一对x和y使得x+y==a,lcm(x,y)==b。数据1e9。

思路:令g=gcd(x,<
1a109
/span>y),则x=k1g,y=k2g并且gcd(k1,k2)==1。

则已知的可以转化成

(k1+k2)∗g=a

k1k2g=b

若gcd(k1,k2)=1。则gcd((k1+k2),k1k2)=1

上面这个是可以证的。具体是证明,加和和k1和k2分别互质,所以和他们的乘积互质。

然后利用这个结论可以得到,g=gcd(a,b),然后可以得到一个方程,解方程即可。

#include <bits/stdc++.h>
using namespace std;
const double pi = acos(-1.0);
long long Zcsqrt(long long x)
{
long long temp = sqrt(x);
temp = max(0LL, temp - 5);
while((temp + 1) * (temp + 1) <= x) temp++;
return temp;
}

int main()
{
int a, b;
while(~scanf("%d%d", &a, &b))
{
int g = __gcd(a, b);
int he = a / g;
int cheng = b / g;
long long delta = 1LL * he * he - 4LL * cheng;
if(delta < 0)   puts("No Solution");
else
{
long long temp = Zcsqrt(delta);
if(temp * temp != delta)    puts("No Solution");
else
{
long long tt1 = he + temp;
long long tt2 = he - temp;
if(tt1 & 1) puts("No Solution");
else
{
long long kk11 = tt2 / 2;
long long kk21 = he - kk11;
printf("%lld %lld\n", kk11 * g, kk21 * g);
}
}
}
}

return 0;
}


hdu 5975 Aninteresting game(树状数组)

题意:有n个集合,第 i 个集合的数是[i-lowbit(i)+1,i-1]+i;有m个询问,询问方式有两种,第一种方式是集合[a,b]共有多少个数?第二种方式是数字x在几个集合里面?(n≤1e18,q≤1e5)

思路:理解树状数组原理的话,这题挺简单的…你会发现每次需要操作的数量刚好就是lowbit(x),那么op1可以用前缀和维护一下,op2就是普通树状数组跳一跳就行了。

#include <bits/stdc++.h>
using namespace std;
long long n, q, mi[105];
long long lowbit(long long x){return x&-x;}
long long solve(long long x)
{
long long ret = 0;
for(int i = 1; i < 63; i++)
{
if(x / mi[i-1] == 0)    break;
ret += (x / mi[i-1] - x / mi[i]) * mi[i-1];
}
return ret;
}

int main()
{
mi[0] = 1;
for(int i = 1; i < 63; i++) mi[i] = mi[i-1] * 2;

while(~scanf("%lld%lld", &n, &q))
{
while(q--)
{
int ty;
long long x, y;
scanf("%d", &ty);
if(ty == 1)
{
scanf("%lld%lld", &x, &y);
printf("%lld\n", solve(y) - solve(x - 1));
}
else
{
scanf("%lld", &x);
long long ans = 0;
while(x <= n)
{
x += lowbit(x);
ans++;
}
printf("%lld\n", ans);
}
}
}
return 0;
}


hdu 5976 Detachment(贪心)

题意: 给定一个自然数x,让你给出一种拆分方式x=a1+a2+⋯(两两两各不相同),使得每个小部分的乘积s=a1∗a2∗⋯最大(x≤1e9,1e6组测试数据)

思路:很容易发现贪心策略,找到一个2为首项的等差数列,加和不大于x。然后把剩下的部分给等差数列从大到小依次的加,然后我们会发现这个大概是一个log(n)的操作。由于测试数据极多,显然会T。那么我们发现可以使用数学公式计算出答案,然后推推公式,瞎比写就行啦。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int mod = 1e9 + 7;
const int maxn = 1e6 + 5;
long long Zcsqrt(long long x)
{
long long temp = sqrt(x);
temp = max(0LL, temp - 5);
while((temp + 1) * (temp + 1) <= x) temp++;
return temp;
}
LL fac[maxn], invf[maxn];
LL quickpow(LL a, LL b, LL Mod)
{
LL ret = 1;
while(b)
{
if(b & 1)   ret = ret * a % Mod;
b >>= 1;
a = (a * a) % Mod;
}
return ret;
}

void init()
{
int limit = 1000000;
fac[0] = 1;
for(int i = 1; i < maxn; i++)   fac[i] = i * fac[i - 1] % mod;
invf[limit] = quickpow(fac[limit], mod - 2, mod);
for(int i = limit - 1; i >= 0; i --) invf[i] = invf[i + 1] * (i + 1) % mod;
}

int main()
{
init();
int T;
scanf("%d", &T);
while(T--)
{
int n;
scanf("%d", &n);
if(n <= 4)
{
printf("%d\n", n);
continue;
}
long long temp = Zcsqrt(9LL + 8LL * n);
temp = temp - 1;
temp /= 2;
temp = max(0LL, temp - 5);
while((temp + 1+ 2) * (temp + 1- 1) <= 2LL * n)   temp++;
long long sum = (temp + 2) * ( temp - 1) / 2;
long long delta = n - sum;
int head = 2, tail = temp;
long long times = delta / (temp - 1);
head += times, tail += times;
delta = delta - times* (temp - 1);
int mr = tail - delta + 1;
int ml = mr - 1;
long long ans = fac[ml] * invf[head-1] % mod;
mr++, tail++;
ans = (ans * fac[tail] % mod * invf[mr-1]) % mod;
printf("%lld\n", ans);
}
return 0;
}


hdu 5977 Garden of Eden(树分治)

题意:给一棵节点数为n,节点种类为k的无根树,问其中有多少种不同的简单路径,可以满足路径上经过所有k种类型的点?(a->b与b->a算作两条路径,起点与终点也可以相同)。n≤1e5,k≤10。

思路:我们思考如果针对,过根节点的路径该怎么处理。我们显然处理出来所有从根结点到各个结点的路径的状态。随便搜一下就行了,然后我们就能知道类似例题1中的depth[u]+depth[v],我们枚举类似depth[u]的路径状态,然后去找到有几条路径状态能和他或起来得到maxState。这时候枚举子集即可,maxState^子集就是depth[v]了。那么我们就能把calc函数写完,如果你搞懂了例题1的话,就会发现直接套一层一样的东西即可。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 50000 + 5;
int n, k, root, a[maxn], siz[maxn], f[maxn], done[maxn];
vector<int>G[maxn];
//找一个点:删去后,使得最大树 最小。
int getRoot(int u, int fa)
{
siz[u] = 1, f[u] = 0;
for(auto v: G[u])   if(v != fa && !done[v])
{
siz[u] += getRoot(v, u);
f[u] = max(f[u], siz[v]);
}
f[u] = max(f[u], f[0] - f[u]);
if(f[u] < f[root])  root = u;
return siz[u];
}
vector<int>vec;
void dfs_state(int cur, int fa, int state)
{
vec.push_back(state);
for(auto v :G[cur]) if(v != fa && !done[v])
{
dfs_state(v, cur, state | (1 << a[v]));
}
}
int maxState, cnt[(1<<10) + 5];
long long calc(int cur, int paState)
{
vec.clear();
dfs_state(cur, -1, paState | (1 << a[cur]));
for(int i = 0; i <= maxState; i++)  cnt[i] = 0;
for(auto o : vec)   cnt[o]++;

long long ret = 0;
for(auto S : vec)
{
ret += cnt[maxState];
for(int i = S; i >= 1; i = (i - 1) & S)
{
ret += cnt[maxState ^ i];
}
}
return ret;
}
long long ans;
void solve(int cur)
{
ans += calc(cur, 0);
done[cur] = 1;
for(auto v : G[cur])
{
if(done[v]) continue;
ans -= calc(v, (1 << a[cur]));
f[0] = siz[v];
getRoot(v, root = 0);
solve(root);
}
}

int main()
{
while(~scanf("%d%d", &n, &k))
{
for(int i = 1; i <= n; i++) done[i] = 0, G[i].clear();
for(int i = 1; i <= n; i++) scanf("%d", &a[i]), a[i]--;
maxState = (1 << k) - 1;
for(int i = 0; i < n - 1; i++)
{
int u, v;
scanf("%d%d", &u, &v);
G[u].push_back(v);
G[v].push_back(u);
}
if (k == 1) {
printf("%lld\n", 1LL * n * n);
continue;
}
f[0] = n;
getRoot(1, root=0);
ans = 0;
solve(root);
printf("%lld\n", ans);
}
return 0;
}


hdu 5978 To begin or not to begin

题意:给出 n 个黑球,一个红球。抽出红球胜利。如果先手有优势 输出 1 没有优势 输出 2 机会均等输出 0

思路:随便算算就发现….每一次抽都是等概率获胜的,看一下奇偶性,也就是两个人各自抽了几次即可。

hdu 5979 Convex

题意:一个凸多边形每个顶点到其原点的距离相等,且能把该多边形分成几个部分,给定每个部分的角度,求多边形面积。

思路:…两边夹一角算三角形会吗,加和即可。

hdu 5980 Find Small A

题意:给N个数 每个数都可以拆开成一个32位的2进制 每八位一个字节 每个字节的2进制数换算成十进制的看有多少个97

思路:进制转换。water。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: