杭电多校第七场部分题题解

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,k1],也就是小明平均分配学习之后还剩下 ( 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,n1],那么小明剩下的学习时间就能分配在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=an+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,k1]F(S)=(nk+1)a1; 当 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)=(nk+1)a+(b(k1))1=(nk+1)a+bk
    • 接下来我们再反过来,求上面函数F的反函数就可以啦,
      设G(m)为总共有m分时小明需花费的最小学习时间,则 m % ( n − k + 1 ) = = n − k m\%(n-k+1)==n-k m%(nk+1)==nk时, G ( m ) = ( m + 1 ) / ( n − k + 1 ) ∗ n G(m)=(m+1)/(n-k+1)*n G(m)=(m+1)/(nk+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/(nk+1)n)+(m%(nk+1)+k)
  • 代码

#include
#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
#pragma GCC optimize(3)

#include
#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) (1pi)概率掉到 x i x_i xi级,给q(5e5)个询问,从 l e i 级 升 到 r i i le_i级升到ri_i leirii级所需金币的期望

  • 分析:设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+(1pi)(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]](1pi)),然后算就可以了

  • 代码

#include
#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.放弃
*/

:

你可能感兴趣的:(学习笔记,ACM)