如果在原点 , 需要 0 步
如果距离是整数 , 需要 1 步
注意到沿对角线和沿直线移动只需要一步
所以最多只需要 2 步
#include
using namespace std;
std::mt19937 rng(std::random_device{}());
typedef long double ld;
typedef long long ll;
typedef unsigned long long ull;
typedef const int& cint;
typedef const ll& cll;
typedef pair<int, int> pii;
typedef pair<int, ll> pil;
#define ls (loc<<1)
#define rs ((loc<<1)|1)
const int mod1 = 1e9+7;
const int mod2 = 998244353;
const int inf_int = 0x7fffffff;
const int hf_int = 0x3f3f3f3f;
const ll inf_ll = 0x7fffffffffffffff;
const double ept = 1e-9;
int x, y;
void solve(cint T) {
cin >> x >> y;
if(x == 0 && y == 0) { cout << 0 << '\n'; return; }
int r = x*x + y*y;
if(int(sqrt(r))*int(sqrt(r)) == r) { cout << 1 << '\n'; return; }
cout << 2 << '\n';
return;
}
int main() {
//freopen("1.in", "r", stdin);
//cout.flags(ios::fixed); cout.precision(8);
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int T_=1;
std::cin >> T_;
for(int _T=1; _T<=T_; _T++)
solve(_T);
return 0;
}
注意到 , 对于任意一种方案 , 将 + x +x +x 向前移动 (如果可以) 会使答案更优
这指明了一种贪心策略 , 能加就加否则减
#include
using namespace std;
std::mt19937 rng(std::random_device{}());
typedef long double ld;
typedef long long ll;
typedef unsigned long long ull;
typedef const int& cint;
typedef const ll& cll;
typedef pair<int, int> pii;
typedef pair<int, ll> pil;
#define ls (loc<<1)
#define rs ((loc<<1)|1)
const int mod1 = 1e9+7;
const int mod2 = 998244353;
const int inf_int = 0x7fffffff;
const int hf_int = 0x3f3f3f3f;
const ll inf_ll = 0x7fffffffffffffff;
const double ept = 1e-9;
ll n, b, x, y;
void solve(cint T) {
ll ans = -inf_ll;
ll sum = 0;
ll now = 0;
cin >> n >> b >> x >> y;
for(int i=1; i<=n; i++) {
if(now + x <= b) { now += x; }
else { now -= y; }
sum += now;
}
cout << sum << '\n';
}
int main() {
freopen("1.in", "r", stdin);
//cout.flags(ios::fixed); cout.precision(8);
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int T_=1;
std::cin >> T_;
for(int _T=1; _T<=T_; _T++)
solve(_T);
return 0;
}
注意题目中要的是最短的满足条件的前缀
所以如果开头是 (
, 那么无论下一个是什么都满足条件
如果开头是 )
, 那么不可能形成合法的括号序 , 必须要形成回文串 , 所以结尾必是 )
由于是最短的 , 所以只需要找到除开头外第一个 )
的位置就可以了
模拟即可
#include
using namespace std;
std::mt19937 rng(std::random_device{}());
typedef long double ld;
typedef long long ll;
typedef unsigned long long ull;
typedef const int& cint;
typedef const ll& cll;
typedef pair<int, int> pii;
typedef pair<int, ll> pil;
#define ls (loc<<1)
#define rs ((loc<<1)|1)
const int mod1 = 1e9+7;
const int mod2 = 998244353;
const int inf_int = 0x7fffffff;
const int hf_int = 0x3f3f3f3f;
const ll inf_ll = 0x7fffffffffffffff;
const double ept = 1e-9;
int n;
char s[500500];
void solve(cint T) {
cin >> n;
cin >> s;
int i = 0;
int sum = 0;
while(i < n-1) {
if(s[i] == '(') { sum++; i += 2; }
else {
bool flag = false;
for(int j=i+1; j<n; j++) {
if(s[j] == ')') { flag = true; sum++; i=j+1; break; }
}
if(!flag) { break; }
}
}
cout << sum << ' ' << max(0, n-i) << '\n';
}
int main() {
freopen("1.in", "r", stdin);
//cout.flags(ios::fixed); cout.precision(8);
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int T_=1;
std::cin >> T_;
for(int _T=1; _T<=T_; _T++)
solve(_T);
return 0;
}
首先先把式子写出来 , 设 s i s_i si 代表选第 i 种时选了 s i s_i si 人
可以得到当玩家选了第 i 种人时可以获得胜利时满足的不等式
h i D j > H j s i d i \cfrac{h_i}{D_j} > \cfrac{H_j}{s_id_i} Djhi>sidiHj
即
s i h i d i > D j H j s_ih_id_i > D_j H_j sihidi>DjHj
可以发现 i 和 j 分离了 , 所以可以分别处理 , 记 a i = h i d i a_i=h_id_i ai=hidi , b j = D j H j b_j = D_jH_j bj=DjHj
显然 , 对于 s i s_i si 相同的单位 , 肯定要选择 a i a_i ai 更大的 , 而 C C C 的范围只有 1e6
立即可以得到一种方法 , 枚举每一个 k ∈ C k \in C k∈C , 计算 c i = k c_i = k ci=k 的单位在买了 1, 2, …, t 次下可以满足的范围
最后做一个前缀最大值 , 对于询问二分查找
复杂度是调和级数
#include
using namespace std;
std::mt19937 rng(std::random_device{}());
typedef long double ld;
typedef long long ll;
typedef unsigned long long ull;
typedef const int& cint;
typedef const ll& cll;
typedef pair<int, int> pii;
typedef pair<int, ll> pil;
#define ls (loc<<1)
#define rs ((loc<<1)|1)
const int mod1 = 1e9+7;
const int mod2 = 998244353;
const int inf_int = 0x7fffffff;
const int hf_int = 0x3f3f3f3f;
const ll inf_ll = 0x7fffffffffffffff;
const double ept = 1e-9;
int n, m, C;
ll c[300300], h[300300], d[300300];
ll D[300300], H[300300];
ll mx[1000100];
ll val[1000100];
void solve(cint T) {
cin >> n >> C;
for(int i=1; i<=n; i++) { cin >> c[i] >> d[i] >> h[i]; }
for(int i=1; i<=n; i++) { mx[c[i]] = max(mx[c[i]], d[i]*h[i]); }
for(int i=1; i<=C; i++) {
if(mx[i]) {
for(int j=1; i*j<=C; j++) {
val[i*j] = max(val[i*j], mx[i]*j);
}
}
}
for(int i=1; i<=C; i++) { val[i] = max(val[i], val[i-1]); }
cin >> m;
ll nd, nh, now;
for(int i=1; i<=m; i++) {
cin >> nd >> nh;
now = nd * nh;
int r = upper_bound(val+1, val+1+C, now) - val;
if(r <= C) { cout << r << '\n'; }
else { cout << -1 << '\n'; }
}
}
int main() {
freopen("1.in", "r", stdin);
//cout.flags(ios::fixed); cout.precision(8);
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int T_=1;
//std::cin >> T_;
for(int _T=1; _T<=T_; _T++)
solve(_T);
return 0;
}
首先可以发现依照题目所说 , 所有连向 1 的边需要形成一棵最小生成树 , 记为 (*)
那么考虑任取两个点 i, j (不等于 1) , 1ij(和连接它们的三条边)构成一个三角形 , 由于 (*) 的存在 , 可以得到 d 1 i ≤ d i j d_{1i} \leq d_{ij} d1i≤dij 并且 d 1 j ≤ d i j d_{1j} \leq d_{ij} d1j≤dij
由于取点的任意性 , 可以得到一个点(不为 1 , 记为 a )连接的所有边中的最短边是连向 1 的边
又因为一条不以 1 为端点的边会被类似 a 的点考虑两次 , 这启发我们从大到小分配边权 , 这样后面分配时就不用考虑前面分配过的边
令 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示当前和 1 相连的边已经被使用了 i 条 , 且目前分配过的最小值是 j (注意实际分配了 j 值的边数可能为 0 , 也就是做了前缀优化)
我们不考虑枚举下一条边使用了哪个值 , 因为当多条边使用同一个值时会很复杂 , 我们考虑有多少边使用值 j − 1 j-1 j−1
令 l l l 为我们枚举的分配了 j − 1 j-1 j−1 的边数 , 可以得到转移方程
d p [ i + l ] [ j − 1 ] = ∑ d p [ i ] [ j ] ⋅ ( n − i − 1 l ) ⋅ p o w ( m a x _ v a l − j + 2 , l ( n − 2 ) − ( l 2 ) − l ⋅ i ) dp[i+l][j-1] =\sum dp[i][j] \cdot \binom{n-i-1}{l} \cdot \mathrm{pow}(\mathrm{max\_val}-j+2\ ,\ l(n-2)-\binom{l}{2}-l\cdot i) dp[i+l][j−1]=∑dp[i][j]⋅(ln−i−1)⋅pow(max_val−j+2 , l(n−2)−(2l)−l⋅i)
其中 p o w ( a , b ) = a b \mathrm{pow}(a,b) = a^b pow(a,b)=ab , m a x _ v a l \mathrm{max\_val} max_val 代表最大可使用的值 , 即题中所述的 k k k .
初始值为 d p [ 0 ] [ m a x _ v a l + 1 ] = 1 dp[0][\mathrm{max\_val}+1] = 1 dp[0][max_val+1]=1 .
解释一下
第二项的二项式系数 , 代表我们从还剩的连向 1 的边中要选择几条分配值 j − 1 j-1 j−1
第三项的幂次 , 代表给所有会被这次选的连向 1 的边的那个非 1 顶点考虑到的不连向 1 的边赋值 j − 1 ∼ m a x _ v a l j-1 \sim \mathrm{max\_val} j−1∼max_val
l ( n − 2 ) l(n-2) l(n−2) 是所有相关的边的数量 , ( l 2 ) \binom{l}{2} (2l) 是前面一项里重复考虑的边的数量 , l ⋅ i l\cdot i l⋅i 是前面已经考虑过的边的数量
细节看代码
#include
using namespace std;
std::mt19937 rng(std::random_device{}());
typedef long double ld;
typedef long long ll;
typedef unsigned long long ull;
typedef const int& cint;
typedef const ll& cll;
typedef pair<int, int> pii;
typedef pair<int, ll> pil;
#define ls (loc<<1)
#define rs ((loc<<1)|1)
const int mod1 = 1e9+7;
const int mod2 = 998244353;
const int inf_int = 0x7fffffff;
const int hf_int = 0x3f3f3f3f;
const ll inf_ll = 0x7fffffffffffffff;
const double ept = 1e-9;
int n, k;
ll dp[300][300];
ll C[303][303];
ll ksm(ll bs, int x) {
ll ans = 1;
while(x) {
if(x & 1) ans = (ans * bs) % mod2;
bs = (bs * bs) % mod2;
x >>= 1;
}
return ans;
}
ll bino(ll x, ll y) {
return C[x][y];
}
void init() {
int t = 300;
for(int i=0; i<=t; i++) { C[i][i] = 1; }
for(int i=0; i<=t; i++) { C[i][0] = 1; }
for(int i=1; i<=t; i++) {
for(int j=1; j<=i; j++) {
C[i][j] = (C[i-1][j] + C[i-1][j-1]) % mod2;
}
}
}
void solve(cint T) {
cin >> n >> k;
dp[0][k+1] = 1;
for(int i=0; i<=n-2; i++) {
for(int j=k+1; j>=2; j--) {
if(dp[i][j]) {
for(int l=n-1-i; l>=0; l--) {
int r = k-j+2;
dp[i+l][j-1] += (dp[i][j] * (bino(n-1-i, l) * ksm(r, l*(n-2)-bino(l, 2)-l*i) % mod2)) % mod2;
dp[i+l][j-1] %= mod2;
}
}
}
}
ll ans = 0;
for(int i=1; i<=k; i++) { ans = (ans + dp[n-1][i]) % mod2; }
cout << ans << '\n';
}
int main() {
freopen("1.in", "r", stdin);
//cout.flags(ios::fixed); cout.precision(8);
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int T_=1;
//std::cin >> T_;
init();
for(int _T=1; _T<=T_; _T++)
solve(_T);
return 0;
}