1006.对抗性-贪心-反函数
-
题目:Final Exam
-
链接:http://acm.hdu.edu.cn/contests/contest_showproblem.php?pid=1006&cid=854
-
大意:有n(1e9)个问题,出题人会在每个问题分配一些分数a[i]∈[0,m], ∑ a [ i ] = m \sum a[i]=m ∑a[i]=m,m∈[1,1e9],小明同学可以在每个问题上分配一些学习时间b[i],对于问题i,当且仅当 b [ i ] > a [ i ] b[i] > a[i] b[i]>a[i]时,小明能答出第i道题,小明想要至少答出k(k∈[1,1e9])道题,求满足要求的 min ( ∑ b ) \min(\sum b) min(∑b)
-
分析:
- 这题我们要用对抗性来想,假设我们是那个狡诈的出题人,小明昨天的学习情况(b[i]数列)对我们来说是已知的,我们想要用最少的总分数m,来卡住小明,让小明不能做出k道题目.
- 我们怎么卡呢?我们将b数组排个序,b[1]是小明花费时间最多的,b[n]是花费时间最少的,我们让小明的学习达到最小的效益,所以我们在小明花费时间最长的k-1道题目上分配0分,哈哈,然后对于b[k~n],我们使 a [ i ] = b [ i ] ( i ∈ [ k , n ] ) a[i]=b[i](i ∈[k,n]) a[i]=b[i](i∈[k,n]),这样我们就可以做到卡住小明的同时总m最小了
- 然后我们就可以再反过来想,小明也不是傻子,知道狡诈的出题人会为用上述方表达他"特别的爱"。于是小明会花费尽量少的时间在前[1,k-1]个问题上,于是小明会采取一种尽量平均的学习策略。
- 我们分类想一下,设小明同学花费的总时间为S。
- 如果S%n==0,那么小明为了使[出题人至少需要的m]最大,肯定要让b[1,k-1]的和最小,这样 ∑ i = k n b [ i ] = m \sum_{i=k}^{n}b[i]=m ∑i=knb[i]=m最大了,所以小明每个问题学习S/n。
- 第二种情况,如果 ( S % n ) ∈ [ 1 , k − 1 ] (S\%n)∈[1,k-1] (S%n)∈[1,k−1],也就是小明平均分配学习之后还剩下 ( S % n ) (S\%n) (S%n)的学习时间,由于要保持b数组单调递减的性质,所以剩下的 ( S % b ) (S\%b) (S%b)的学习时间只能分配在b[1~k]上,因此 ∑ i = k n b [ i ] = m \sum_{i=k}^{n}b[i]=m ∑i=knb[i]=m不变。
- 如果 ( S % n ) ∈ [ k , n − 1 ] (S\%n)∈[k,n-1] (S%n)∈[k,n−1],那么小明剩下的学习时间就能分配在b[k]至b[n]上了,在b[k]至b[n]分配的学习时间就是m的增量。
- 综上,设F(S)为小明(有策略地)学习了 S = a ∗ n + b , a ∈ [ 0 , ∞ ) , b ∈ [ 0 , n ) S=a*n+b,a∈[0,∞),b∈[0,n) S=a∗n+b,a∈[0,∞),b∈[0,n)小时时,出题人仍然不能卡住小明的最大分数m,那么 当 b ∈ [ 0 , k − 1 ] 时 , F ( S ) = ( n − k + 1 ) ∗ a − 1 当b∈[0,k-1]时,F(S)=(n-k+1)*a-1 当b∈[0,k−1]时,F(S)=(n−k+1)∗a−1; 当 b ∈ [ k , n ) 时 , F ( S ) = ( n − k + 1 ) ∗ a + ( b − ( k − 1 ) ) − 1 = ( n − k + 1 ) ∗ a + b − k 当b∈[k,n)时,F(S)=(n-k+1)*a+(b-(k-1))-1=(n-k+1)*a+b-k 当b∈[k,n)时,F(S)=(n−k+1)∗a+(b−(k−1))−1=(n−k+1)∗a+b−k。
- 接下来我们再反过来,求上面函数F的反函数就可以啦,
设G(m)为总共有m分时小明需花费的最小学习时间,则 m % ( n − k + 1 ) = = n − k m\%(n-k+1)==n-k m%(n−k+1)==n−k时, G ( m ) = ( m + 1 ) / ( n − k + 1 ) ∗ n G(m)=(m+1)/(n-k+1)*n G(m)=(m+1)/(n−k+1)∗n;否则, G ( m ) = ( m / ( n − k + 1 ) ∗ n ) + ( m % ( n − k + 1 ) + k ) G(m)=(m/(n-k+1)*n)+(m\%(n-k+1)+k) G(m)=(m/(n−k+1)∗n)+(m%(n−k+1)+k)
-
代码
#include<bits/stdc++.h>
#define F(i,a,b) for(int i=(a);i<=int(b);++i)
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = int(20);
#define endl '\n'
ll n, m, k;
int main()
{
#ifndef endl
freopen("C:\\Users\\VULCAN\\Desktop\\data.in", "r", stdin);
cout << "************************************Local Test*********************************" << endl;
#endif // !endl
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int T(1), cas(0);
cin >> T;
while (cas, T--)
{
ll ans;
cin >> n >> m >> k;
ll mo= (n - k + 1);
ll shang = m / (n - k + 1);
ll yu = m % (n - k + 1);
if (yu==mo-1)
{
ans = (m + 1) / mo * n;
}
else
{
ans = shang * n + (yu + k);
}
ans = min(ans, (m + 1)*k);
cout << ans << endl;
}
return 0;
}
//What to Debug
/*
-1.最好把全部warning都X掉,否则:https://vjudge.net/solution/19887176
0.看看自己是否有可能需要快读,禁endl
1.数组越界,爆int,浮点精度(查看精度是否达到题目要求,看有没有浮点数比较:eps),取模操作,初始化数组,边缘数据,输出格式(cas),强制在线是否更新了las
2.通读代码,代码无逻辑错误
3.读题,找到题意理解失误或算法错误
4.放弃
*/
1010.不平等博弈-有点水的题
-
题目:Just Repeat
-
链接:http://acm.hdu.edu.cn/contests/contest_showproblem.php?pid=1010&cid=854
-
大意:两个人玩牌,2人分别由n,m张牌,每张牌有一个颜色,两人轮流出牌,且对于颜色c,如果如果对方出过颜色c的牌,那自己就不能再出颜色c的牌,先不能出牌的输(包括牌出完了和自己的牌的颜色都是对方出过的)
-
分析:对于颜色c,如果只有一个人有这种颜色的牌,那这个人的步数就加上它拥有这种颜色的牌的数量。如果两个人都有这种颜色的牌,那假如先手有 c i c_i ci张c,后手有 d i d_i di张c,那第一打出颜色c的牌的人就能产生 c i + d i c_i+d_i ci+di的贡献(包括增加自己的步数,和减少对方的步数),于是接下来贪心地选 ( c i + d i ) (c_i+d_i) (ci+di)最大的颜色就好了。顺便说一句:颜色的值很大,不能用数组;也不能用map,会卡常,我用的unordered_map
#include<bits/stdc++.h>
#pragma GCC optimize(3)
#include<unordered_map>
#define F(i,a,b) for(int i=(a);i<=int(b);++i)
using namespace std;
typedef unsigned long long ull;
ull k1, k2, mod;
ull rng() {
ull k3 = k1, k4 = k2;
k1 = k4;
k3 ^= k3 << 23;
k2 = k3 ^ k4 ^ (k3 >> 17) ^ (k4 >> 26);
return k2 + k4;
}
unordered_map<ull, int>ma[2];
struct node
{
int a, b, sum;
bool operator<(const node&ri)const
{
return sum < ri.sum;
}
};
vector<node>ns;
int n[2];
int lead(0);
void prin()
{
if (lead > 0)cout << "Cuber QQ" << '\n';
else cout << "Quber CC" << '\n';
ma[0].clear(); ma[1].clear();
ns.clear();
n[0] = n[1] = 0;
lead = 0;
}
#define endl '\n'
int main()
{
#ifndef endl
freopen("C:\\Users\\VULCAN\\Desktop\\data.in", "r", stdin);
cout << "************************************Local Test*********************************" << endl;
#endif // !endl
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int T(1), cas(0);
cin >> T;
while (cas, T--)
{
int tp;
cin >> n[0];
cin >> n[1];
cin>> tp;
if (tp == 1)
{
F(id, 0, 1)F(i, 1, n[id])
{
ull v; cin >> v;
++ma[id][v];
}
}
else
{
cin >> k1 >> k2 >> mod;
F(i, 1, n[0])
{
ull v= rng() % mod;
ma[0][v]++;
}
cin >> k1 >> k2 >> mod;
F(i, 1, n[1])
{
ull v = rng() % mod;
ma[1][v]++;
}
}
for (auto p0 : ma[0])
{
if (ma[1].find(p0.first) != ma[1].end())
{
ns.push_back({ p0.second,ma[1][p0.first] });
ma[1].erase(p0.first);
}
else
lead += p0.second;
}
for (auto p1 : ma[1])lead -= p1.second;
for (auto&x : ns)x.sum = x.a + x.b;
sort(ns.begin(), ns.end());
reverse(ns.begin(), ns.end());
F(i, 0, ns.size() - 1)
{
if ((i & 1) == 0)
{
lead += ns[i].a;
}
else
lead -= ns[i].b;
}
prin();
}
return 0;
}
//What to Debug
/*
-1.最好把全部warning都X掉,否则:https://vjudge.net/solution/19887176
0.看看自己是否有可能需要快读,禁endl
1.数组越界,爆int,浮点精度(查看精度是否达到题目要求,看有没有浮点数比较:eps),取模操作,初始化数组,边缘数据,输出格式(cas),强制在线是否更新了las
2.通读代码,代码无逻辑错误
3.读题,找到题意理解失误或算法错误
4.放弃
*/
1011.概率DP-水题
-
题目:Kejin Player
-
链接:http://acm.hdu.edu.cn/contests/contest_showproblem.php?pid=1011&cid=854
-
大意:有n(5e5)个级别,玩家刚开始1级,i级时,花费 a i a_i ai金币有 p i p_i pi概率升到i+1级,有 ( 1 − p i ) (1-p_i) (1−pi)概率掉到 x i x_i xi级,给q(5e5)个询问,从 l e i 级 升 到 r i i le_i级升到ri_i lei级升到rii级所需金币的期望
-
分析:设E[i]为从1级升到i级所需金币的期望,有个结论:从i级升到j级所需金币的期望 = E [ j ] − E [ i ] =E[j]-E[i] =E[j]−E[i],所以/ E [ i + 1 ] = E [ i ] + a i + ( 1 − p i ) ∗ ( E [ i + 1 ] − E [ x [ i ] ] ) E[i+1]=E[i]+a_i+(1-p_i)*(E[i+1]-E[x[i]]) E[i+1]=E[i]+ai+(1−pi)∗(E[i+1]−E[x[i]]),所以 E [ i + 1 ] = 1 / p i ∗ ( E [ i ] + a [ i ] − E [ x [ i ] ] ∗ ( 1 − p i ) ) E[i+1]=1/p_i*(E[i]+a[i]-E[x[i]]*(1-p_i)) E[i+1]=1/pi∗(E[i]+a[i]−E[x[i]]∗(1−pi)),然后算就可以了
-
代码
#include<bits/stdc++.h>
#define F(i,a,b) for(int i=(a);i<=int(b);++i)
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = int(5e5+9);
const int mod = 1e9 + 7;
ll MODED(ll x)
{
x %= mod;
if (x < 0)x += mod;
if (x >= mod)x -= mod;
return x;
}
//3_2快速幂-逆元
ll qpow(ll a, ll b, ll p) //求a^bMODp
{
ll ret = 1; a %= p;
while (b)
{
if (b & 1)ret = ret * a % p;
a = a * a % p;
b >>= 1;
}
return ret;
}
////////////////求逆元//////////////////////
////////费马小定理
ll inv(ll a, ll p) //返回a对p的逆元
{
return qpow(a, p - 2, p);
}
int n, q;
int p[maxn], s[maxn], x[maxn], a[maxn];
int E[maxn];
#define endl '\n'
int main()
{
#ifndef endl
freopen("C:\\Users\\VULCAN\\Desktop\\data.in", "r", stdin);
cout << "************************************Local Test*********************************" << endl;
#endif // !endl
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int T(1), cas(0);
cin >> T;
while (cas, T--)
{
cin >> n >> q;
F(i, 1, n)cin >> p[i] >> s[i] >> x[i] >> a[i];
F(i, 1, n)
{
p[i] = 1ll * p[i] * inv(s[i], mod) % mod;
}
E[1] = 0;
F(i, 1, n)
{
E[i + 1] = inv(p[i], mod);
ll tmp = MODED(E[i] + a[i]-1ll*E[x[i]]*(1-p[i])%mod);
E[i + 1] =1ll* E[i + 1] * tmp%mod;
//cout << E[i+1] << endl;
}
F(i, 1, q)
{
int le, ri; cin >> le >> ri;
cout << MODED(E[ri] - E[le]) << '\n';
}
}
return 0;
}
//What to Debug
/*
-1.最好把全部warning都X掉,否则:https://vjudge.net/solution/19887176
0.看看自己是否有可能需要快读,禁endl
1.数组越界,爆int,浮点精度(查看精度是否达到题目要求,看有没有浮点数比较:eps),取模操作,初始化数组,边缘数据,输出格式(cas),强制在线是否更新了las
2.通读代码,代码无逻辑错误
3.读题,找到题意理解失误或算法错误
4.放弃
*/
: