2024CCPC山東邀請賽 IAFCK

nannandbk發表於2024-10-10

2024CCPC山東邀請賽 IAFCK

I. Left Shifting

思路:要第一個和最後一個一樣,那找到第一個連續的兩個一樣的就是答案。如果一開始第一個和最後一個就是一樣的,那就是0。

// AC one more times
// nndbk
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 2e5 + 10;

int main()
{
    ios::sync_with_stdio(false);   cin.tie(nullptr), cout.tie(nullptr);
    int t; cin>>t;
    while(t--)
    {
        string s; cin>>s;
        int n = s.size();
        s = "?"+s;
        if(s[1] == s[n]){
            cout<<"0\n";
            continue;
        }
        bool ok = false;
        for(int i = 1;i < n; i++){
            if(s[i]==s[i+1]){
                ok = true;
                cout<<i<<"\n";
                break;
            }
        }
        if(!ok)cout<<"-1\n";
    }


    return 0;
}

A. Printer

思路:發現存在單調性,並且很好去check,直接上二分。

check部分:

先對於每一個機器,先算出一個執行週期是\(t\times l+w\)。然後對於二分的答案\(x\)時間看能生成多少?

先看它有多少個完整的執行週期,即\(x/(t\times l+w)\),完整的執行週期生產\(x/(t\times l+w)\times l\)

再看第二部分剩餘的時間\(rem = x-x/(t\times l + w)\times (t\times l+w)\)

如果\(rem > t\times l\)了那麼\(rem\)不能全去做生成,並且也不夠一個完整週期,所以這段時間生成的一定是\(l\)個。

否則的話,這段剩餘時間生產的數量就是\(rem/t\)個。

最後兩部分加起來,如果\(\ge k\)則滿足條件。

要小心的是:注意這題可能寫的時候會爆long long小心一點,或者直接開int128。還有就是注意二分上界是\(2e18\)(極限情況\(t = 1,l = 1e9,w =1e9,k = 1e9\),則至少要(\((1e9+1e9)\times 1e9 = 2e18\))。

// AC one more times
// nndbk
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 2e5 + 10;

struct ty
{
    ll t,l,w;
}a[N];

ll n,k;
bool judge(ll x)
{
    __int128 ans = 0;
    for(int i = 1;i <= n; i++)
    {
        __int128 t = a[i].t,l = a[i].l,w = a[i].w;
        __int128 res = x/(t*l+w)*l;
 
        __int128 rem = x-x/(t*l+w)*(t*l+w);

        if(rem > t*l)
            res += l;
        else
            res += rem/t;
 
        ans += res;
    }
    return ans >= k;
}

int main()
{
    ios::sync_with_stdio(false);   cin.tie(nullptr), cout.tie(nullptr);
    int t; cin>>t;
    while(t--)
    {
         cin>>n>>k;
        for(int i = 1;i <= n; i++)
        {
            cin>>a[i].t>>a[i].l>>a[i].w;
        }

        __int128 l = 0,r = 2e18;
        while(l <= r)
        {
            __int128 mid = (l+r)>>1;
            if(judge(mid))r = mid-1;
            else l = mid + 1;
        }

        cout<<(ll)r+1<<'\n';
    }

    return 0;
}

F. Divide the Sequence

思路:首先看到資料範圍那麼大,感覺是個結論的數學題或者找規律那種。然後開始推式子,對\(\sum_{i = 1}^ki\times s_i\)這個式子進行變換。

我們知道分成若干個區間:\([l_1,r_1],[l_2,r_2],...,[l_k,r_k]\)

結合字首和思想就是:\(\sum_{i = 1}^ki\times s_i=1\times(s[r_1]-s[l_1-1])+2\times(s[r_2]-s[l_2-1])+3\times(s[r_3]-s[l_3-1])+...+k\times(s[r_k]-s[l_k-1])\)

又因為這些區間都是連續的,進而可以化簡為:\(\sum_{i = 1}^ki\times s_i=1\times(s[r_1]-s[0])+2\times(s[r_2]-s[r_1])+3\times(s[r_3]-s[r2])+...+k\times(s[r_k]-s[r_{k-1}])\)

展開化簡得:\(\sum_{i = 1}^ki\times s_i=k\times s[n]-s[r_1]-s[r_2]-...-s[r_{k-1}]\)

\(s[n]\)是固定的,我們要結果越大,只需要減去的越小越好,我們找到前\(k-1\)個最小的字首和即可。

// AC one more times
// nndbk
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 5e5 + 10;
ll a[N],s[N];

bool cmp(ll x,ll y)
{
    return x > y;
}

int main()
{
    ios::sync_with_stdio(false);   cin.tie(nullptr), cout.tie(nullptr);
    int t; cin>>t;
    while(t--)
    {
        int n; cin>>n;
        for(int i = 1;i <= n; i++)
            cin>>a[i];
        for(int i = 1;i <= n; i++)
            s[i] = s[i-1]+a[i];

        sort(s+1,s+n);
        ll x = 0;
        for(int i = 1;i <= n; i++){
            cout<<i*s[n]-x<<' ';
            x += s[i];
        }
       
        cout<<"\n";
    }
    return 0;
}

K. Matrix

思路:這題是個構造呀,藕最討厭構造哩,想到就會,想不到就寄(。

因為對於\(1\)~\(2n\)要至少出現一次,並且所有四個角組成的矩形裡面只能有一個四個角都不一樣。

我們可以怎麼構造呢?

以5為例,可以考慮先這樣子把10個數字填完,並且此時已經有一個四個都不一樣的了。現在我們要保證的是其他都存在至少2個一樣。

image

一開始我們想的是:直接填和左邊一樣的,然後發現:

image

但是不對呀,最後一行和第一行組成的出現的四個都不同。怎麼辦呢?考慮最後一排特別。既然它和第一排不一樣,那麼我讓它一樣不就行啦,然後我們得到了:

image

// AC one more times
// nndbk
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 2e3 + 10;
int a[N][N];
int main()
{
    ios::sync_with_stdio(false);   cin.tie(nullptr), cout.tie(nullptr);
    int n; cin>>n;
    if(n<=3)
    {
        cout<<"Yes\n";
        if(n==2)
        {
            cout<<"1 2\n";
            cout<<"3 4\n";
        }

        if(n==3)
        {
            cout<<"3 2 6\n";
            cout<<"4 3 3\n";
            cout<<"3 1 5\n";
        }
    }else{
        cout<<"Yes\n";
        int cnt = 0;
        for(int i = 1;i <= n; i++)
            a[1][i] = ++cnt;
        for(int i = 2;i <= n; i++)
            a[i][1] = ++cnt;

        a[n][n] = ++cnt;
        for(int i = 2;i <= n; i++)
            for(int j = 2;j <= n; j++)
            {
                a[i][j] = a[i][j-1];
            }

        a[n][n] = cnt;
         
        for(int i = 2;i < n; i++)
            a[n][i] = a[1][i];

        for(int i = 1;i <= n; i++){
            for(int j = 1;j <= n; j++)
            {
                cout<<a[i][j]<<" ";
            }
            cout<<"\n";
        }

    }


    return 0;
}

C. Colorful Segments 2

思路:區間覆蓋+組合數學。

因為我們發現後面的貢獻只會受到它前面的情況的約束,那麼我們考慮先按左端點排序。之後呢,第一個的顏色情況肯定是\(k\),我們從第二個開始考慮,先預設是\(k-1\)。如果發現,當前的左端點比之前所有的右端點還要大了,說明沒有覆蓋了,並且那些右端點不會對後面有新的影響了,我們把k++,並且不要再管這個沒有用的右端點了。

也就是說,當前的貢獻,只需考慮前面的右端點。那麼我們可以用一個小根堆去維護右端點。如果當前的左端點比之前出現的右端點大了,那麼把這些不可能再產生影響的右端點pop掉,並且k++。下一次迴圈之前先k--(預設有覆蓋,如果沒有也是會加回來的,所以先減去,注意不要變成負數所以和0取個max)。每算完一個後把貢獻依次乘起來就是答案啦(組合數學,分步乘法)。

// AC one more times
// nndbk
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 998244353;
const int N = 5e5 + 10;
struct ty
{
    int l,r;
}a[N];

bool cmp(ty a,ty b)
{
    return a.l < b.l;
}

int main()
{
    ios::sync_with_stdio(false);   cin.tie(nullptr), cout.tie(nullptr);
    int t; cin>>t;
    while(t--)
    {
        int n,k; cin>>n>>k;
        for(int i = 1;i <= n; i++)
            cin>>a[i].l>>a[i].r;
        sort(a+1,a+1+n,cmp);

        priority_queue<int,vector<int>,greater<int>>q;
        q.push(a[1].r);
        ll ans = k % mod;
        k--;
        for(int i = 2;i <= n; i++)
        {
            while(!q.empty() && q.top() < a[i].l){
                k++;
                q.pop();
            }
            ans = ans*k%mod;
            k--;
            k = max(0,k);
            q.push(a[i].r);
        }
        cout<<ans<<"\n";

    }
    return 0;
}

相關文章