2017 ACM-ICPC Asia Xi‘an Regional Contest

2017 ACM-ICPC Asia Xi’an Regional Contest

自闭线段树专场,啥都不会签到罚坐5h,慢慢补题吧。学长讨论的东西一个都听不懂淦

知识点概况

题目知识点
B - Lovers带删除的二分
F - God of Gamblers找规律,概率
J - LOL组合计数+暴力+剪枝/状压DP
H - Arrangement for Contests贪心+线段树
A - XOR线性基+线段树
G - Sum of xor sumDP+异或子集和+位运算

题目

B - Lovers

在这里插入图片描述
样例

Sample Input
1
3 4
1 2 3
1 2 3
Sample Output
3

题意
对于数组a,b如果a[i]+b[j]>=k,那么配对成功,问最多可以配对多少
思路
a和b排序后,枚举a,找第一个满足条件的条件的j
经典带删除操作的二分,复杂度应该为 O ( N l o g N ) O(NlogN) O(NlogN),应该可以双指针或者multiset实现,由于不怎么用过多重集,所以用vector实现一波
代码

#include<bits/stdc++.h>
using namespace std;
#define endl "\n"
typedef long long LL;
typedef pair<int,int> PII;
#define MULINPUT
/*DATA & KEY
t 1 10
n 1 200000
k 0 1e9
ai 0 1e9
bi 0 1e9
*/
int T;
const int N=200005;
bool vis[N];
void solve(int C)
{
	//NEW DATA CLEAN
	vector<LL>a,b;
	//NOTE!!!
	LL n,k;scanf("%lld %lld",&n,&k);
	for(int i=1;i<=n;i++)
	{
		LL x;scanf("%lld",&x);
		a.push_back(x);
	}
	for(int i=1;i<=n;i++)
	{
		LL x;scanf("%lld",&x);
		b.push_back(x);
	}
		
	sort(a.begin(),a.end());
	sort(b.begin(),b.end());
	LL ans=0;

	for(int i=0;i<n;i++)
	{
		int l=0,r=b.size()-1;
		while(l<r)
		{
			int mid=(l+r)>>1;
			if(a[i]+b[mid]>=k)r=mid;
			else l=mid+1;
		}
		if(a[i]+b[l]>=k)
		{
			ans++;
			b.erase(b.begin()+l);
		}
	}
	cout<<ans<<endl;
}

int main()
{
	#ifdef MULINPUT
		scanf("%d",&T);
		for(int i=1;i<=T;i++)solve(i);
	#else
		solve(1);
	#endif
	return 0;
}

F - God of Gamblers

在这里插入图片描述
样例

Sample Input
1 0
3 3
Sample Output
1.00000
0.50000

题意
看了很久才看懂(英语能力=-INF)
每次有1/2赢的概率,一块钱开始,赢了拿两倍的钱,输了就没了本钱,每次数就翻倍,只要赢了就能拿回之前输的。
思路
直接找的规律hh,输出n/(m+n)即可
代码

#include<bits/stdc++.h>
using namespace std;
#define endl "\n"
typedef long long LL;
typedef pair<int,int> PII;
//#define MULINPUT
/*DATA & KEY
F
*/
int T;
void solve(int C)
{
	//NEW DATA CLEAN
	
	//NOTE!!!
	double n,m;
	while(~scanf("%lf%lf",&n,&m))
	{
		printf("%.5lf\n",n/(m+n));
	}
	
}

int main()
{
	#ifdef MULINPUT
		scanf("%d",&T);
		for(int i=1;i<=T;i++)solve(i);
	#else
		solve(1);
	#endif
	return 0;
}

J - LOL

在这里插入图片描述
样例

Sample Input
0110011100011001001100011110001110001110001010010111111110101010010011010000110100011001001111101011
1000111101111110110100001101001101010001111001001011110001111110101000011101000001011100001001011010
0100101100011110011100110110011100111100010010011001111110101111111000000110001110000110001100001110
1110010101010001000110100011101010001010000110001111111110101010000000001111001110110101110000010011
1000010011111110001101100000101001110100011000111010011111110110111010011111010110101111011111011011
Sample Output
515649254

题意
100个英雄,两只队伍两种操作,ban和pick
ban:每个人都可以随意选择英雄
pick:只能选自己有的
敌方有全部英雄,题目通过5个01串告诉你己方有的英雄
ban和pick的20个英雄不能重复

思路

选英雄应该用排列数,禁英雄应用组合数
考虑答案组成=敌方ban人方案数x敌方pick人方案数x己方ban人x己方pick人方案数
先考虑己方pick人方案数,这样其他的容易考虑
己方pick人方案数:爆搜+剪枝/DP
己方ban人方案数:C(95,5)
敌方ban人方案数:C(90,5)
敌方pick人方案数:A(85,5)

爆搜解法

思考如何爆搜,相当于根据给的5*100的01矩阵,每一行选1个,要求5个1不同列的方案数,如何实现呢?写个锤子DFS,直接循环一套加个条件判断啊!然后就是优化,正常来讲是要for循环五层的,这样铁定超时,我们可以把循环优化到4层,最后一层直接出结果即可

DP解法:

定义 d p [ i ] [ j ] dp[i][j] dp[i][j]为选到第i个英雄的时候,己方队伍选择英雄情况(二进制表示)
状态转移有这么几种
5个人都不选第i个
d p [ i ] [ j ] + = d p [ i − 1 ] [ j ] dp[i][j]+=dp[i-1][j] dp[i][j]+=dp[i1][j]
第1个人选第i个
第2个人选第i个
第3个人选第i个
第4个人选第i个
第5个人选第i个
总结以下就是第k个人选第i个物品
i f ( 第 k 个 人 没 选 过 并 且 这 个 人 可 以 选 i ) d p [ i ] [ j ] = d p [ i − 1 ] [ j − ( 1 < < k ) ] if(第k个人没选过并且这个人可以选i)dp[i][j]=dp[i-1][j-(1<<k)] if(ki)dp[i][j]=dp[i1][j(1<<k)]

j&(1<<i)==1表示第i个人已经选过了
j+(1<<i)表示让第i个人选当前英雄

代码
爆搜解法

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 105, mod = 1e9 + 7;

char a[N], b[N], c[N], d[N], e[N];

LL A(LL N, LL R) {
  LL sum = 1;
  for (int i = 0; i < R; i++) sum *= N - i;
  return sum;
}
LL C(LL N, LL R) {
  LL sum = 1;
  for (int i = 1; i <= R; i++) sum = sum * (N + 1 - i) / i;
  return sum;
}

int main() {
  while (~scanf("%s%s%s%s%s", a, b, c, d, e)) {
    int len = 0;
    for (int i = 0; i < 100; i++)
      if (e[i] == '1') len++;
    LL ans = 0;
    for (int i = 0; i < 100; i++) {
      if (a[i] == '0') continue;
      for (int j = 0; j < 100; j++) {
        if (b[j] == '0' || j == i) continue;
        for (int k = 0; k < 100; k++) {
          if (c[k] == '0' || k == i || k == j) continue;
          for (int p = 0; p < 100; p++) {
            if (d[p] == '0' || p == i || p == j || p == k) continue;
            int res = len;
            if (e[i] == '1') res--;
            if (e[j] == '1') res--;
            if (e[k] == '1') res--;
            if (e[p] == '1') res--;
            if (res >= 0) ans += res;
          }
        }
      }
    }
    cout << C(95, 5) * C(90, 5) % mod * A(85, 5) % mod * ans % mod<<endl;
  }
  return 0;
}

DP解法

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 105, M = 1 << 5, mod = 1e9 + 7;

char s[5][105];
LL dp[N][M];
LL A(LL N, LL R) {
  LL sum = 1;
  for (int i = 0; i < R; i++) sum *= N - i;
  return sum;
}
LL C(LL N, LL R) {
  LL sum = 1;
  for (int i = 1; i <= R; i++) sum = sum * (N + 1 - i) / i;
  return sum;
}

int main() {
  while (
      ~scanf("%s%s%s%s%s", s[0] + 1, s[1] + 1, s[2] + 1, s[3] + 1, s[4] + 1)) {
    memset(dp, 0, sizeof dp);
    dp[0][0] = 1;
    for (int i = 1; i <= 100; i++)  //从前i个英雄里选
      for (int j = 0; j < (1 << 5); j++) {
        dp[i][j] += dp[i - 1][j];  //都不选
        for (int k = 0; k < 5; k++)
          if (j & (1 << k) && s[k][i] == '1')  //可以选并且没选过
          {
            dp[i][j] = (dp[i][j] + dp[i - 1][j - (1 << k)]) % mod;
          }
      }
    LL ans = dp[100][(1 << 5) - 1] % mod;
    cout << C(95, 5) * C(90, 5) % mod * A(85, 5) % mod * ans % mod << endl;
  }
  return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值