E2. Close Tuples (hard version)
题目大意:
给
定
一
个
长
度
为
n
的
序
列
a
,
给定一个长度为n的序列a,
给定一个长度为n的序列a,
要
从
中
挑
选
一
个
m
元
组
(
a
i
1
,
a
i
2
,
a
i
3
.
.
.
a
i
m
)
[
i
1
<
i
2
<
i
3
.
.
.
.
<
i
m
]
要从中挑选一个m元组(ai_1,ai_2,ai_3...ai_m)[i_1 < i_2 < i_3....<i_m]
要从中挑选一个m元组(ai1,ai2,ai3...aim)[i1<i2<i3....<im]满足:
问
有
多
少
种
这
样
的
组
合
?
问有多少种这样的组合?
问有多少种这样的组合?
解题思路:
首
先
我
们
不
能
被
后
面
下
标
的
限
制
所
迷
惑
它
只
是
想
表
达
数
字
不
能
被
重
复
选
择
而
已
,
首先我们不能被后面下标的限制所迷惑它只是想表达数字不能被重复选择而已,
首先我们不能被后面下标的限制所迷惑它只是想表达数字不能被重复选择而已,
那
么
我
们
可
以
把
a
数
组
进
行
排
序
,
这
样
就
好
选
一
点
,
在
排
序
好
的
数
组
中
,
我
们
那么我们可以把a数组进行排序,这样就好选一点,在排序好的数组中,我们
那么我们可以把a数组进行排序,这样就好选一点,在排序好的数组中,我们
把
对
每
个
a
i
算
一
下
贡
献
,
但
是
我
们
为
了
不
让
序
列
重
复
统
计
我
们
可
以
这
样
把对每个ai算一下贡献,但是我们为了不让序列重复统计我们可以这样
把对每个ai算一下贡献,但是我们为了不让序列重复统计我们可以这样
假
设
a
i
是
长
度
为
m
的
序
列
中
的
最
大
值
,
那
么
我
们
就
可
以
从
假设a_i是长度为m的序列中的最大值,那么我们就可以从
假设ai是长度为m的序列中的最大值,那么我们就可以从
[
1
,
i
−
1
]
位
置
里
面
找
到
第
一
个
a
j
使
得
a
j
≤
a
i
&
&
a
i
−
a
j
≤
k
所
以
我
们
就
可
以
保
证
序
列
可
以
不
被
重
复
计
算
,
假
设
那
个
位
置
是
p
o
s
,
那
么
我
们
要
保
证
最
大
值
一
定
要
被
选
的
情
况
下
在
[
p
o
s
,
i
−
1
]
里
面
挑
m
−
1
个
数
就
可
以
了
[1,i-1]位置里面找到第一个a_j使得a_j\leq a_i\&\&ai-aj \leq k所以我们就可以保证序列可以不被重复计算,假设那个位置是pos,那么我们要保证最大值一定要被选的情况下在[pos,i-1]里面挑m-1个数就可以了
[1,i−1]位置里面找到第一个aj使得aj≤ai&&ai−aj≤k所以我们就可以保证序列可以不被重复计算,假设那个位置是pos,那么我们要保证最大值一定要被选的情况下在[pos,i−1]里面挑m−1个数就可以了
a
n
s
=
∑
i
=
1
n
C
i
−
p
o
s
m
−
1
ans=\sum_{i=1}^{n} C_{i-pos}^{m-1}
ans=i=1∑nCi−posm−1
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
const int mod = 1e9+7;
const int N = 200010;
#define ll long long
int n, T;
int a[N];
int m, k;
ll quick_pow(int a,int b)
{
ll res = 1;
while(b)
{
if(b&1) res=res*a%mod;
a=(ll)a*a%mod;
b>>=1;
}
return res;
}
ll C(int n, int m)
{
if(n<m) return 0;
ll up = 1, down = 1;
for(int i=n;i>n-m;i--) up=up*i%mod;
for(int i=1;i<=m;i++) down=down*i%mod;
down = quick_pow(down,mod-2);
return up*down%mod;
}
int main()
{
ios::sync_with_stdio(false);
cin>>T;
while(T--)
{
cin>>n>>m>>k;
for(int i=1;i<=n;i++) cin>>a[i];
sort(a+1,a+n+1);
ll ans = 0;
for(int i=1;i<=n;i++)
{
int pos = lower_bound(a+1,a+i+1,a[i]-k) - a;
ans = (ans + C(i - pos, m - 1))%mod;
}
cout<<ans<<endl;
}
return 0;
}
题目大意:
就
是
给
你
一
个
圆
形
状
的
区
域
,
两
个
人
从
(
0
,
0
)
点
开
始
每
次
移
动
一
个
棋
子
向
上
或
者
向
右
移
动
k
步
,
最
后
谁
把
就是给你一个圆形状的区域,两个人从(0,0)点开始每次移动一个棋子向上或者向右移动k步,最后谁把
就是给你一个圆形状的区域,两个人从(0,0)点开始每次移动一个棋子向上或者向右移动k步,最后谁把
棋
子
移
动
出
了
圆
形
区
域
就
输
了
,
问
你
最
后
是
谁
赢
了
棋子移动出了圆形区域就输了,问你最后是谁赢了
棋子移动出了圆形区域就输了,问你最后是谁赢了
解题思路:
1.
通
过
观
察
可
以
知
道
这
是
一
个
对
称
博
弈
那
么
我
们
只
要
考
虑
中
间
对
角
线
的
位
置
的
胜
负
状
态
因
为
x
和
y
都
是
增
加
,
如
果
对
角
线
是
必
败
态
那
么
先
手
必
定
会
远
离
,
但
是
后
手
会
尽
可
能
的
去
靠
近
对
角
线
,
1.通过观察可以知道这是一个对称博弈那么我们只要考虑中间对角线的位置的胜负状态因为x和y都是增加,如果对角线是必败态那么先手必定会远离,但是后手会尽可能的去靠近对角线,
1.通过观察可以知道这是一个对称博弈那么我们只要考虑中间对角线的位置的胜负状态因为x和y都是增加,如果对角线是必败态那么先手必定会远离,但是后手会尽可能的去靠近对角线,
2.
同
理
可
得
2.同理可得
2.同理可得
那
么
我
们
只
要
判
断
对
角
线
上
的
最
后
的
点
的
状
态
:
那么我们只要判断对角线上的最后的点的状态:
那么我们只要判断对角线上的最后的点的状态:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int T;
ll d, k;
int main() {
cin >> T;
while(T--) {
cin >> d >> k;
ll x = 0;
while(2ll * (x + k) * (x + k) <= d * d) x += k;//判断最靠近边界的状态
if((x + k) * (x + k) + x * x <= d * d) {
printf("Ashish\n");
} else {
printf("Utkarsh\n");
}
}
return 0;
}