1. 程式人生 > >2018 年山東省第九屆ACM省賽部分題解

2018 年山東省第九屆ACM省賽部分題解

A題 貪心瞎搞

看第二個樣例
s:ELLY t:KRIS
先對兩個字串排序 如果s[i]<=t[i]直接轉換,否則KRS部分迴圈一下求解
1.ELLY IKRS
2.ELLY IRSK
3.ELLY ISKR
求這三種情況的最小花費就好了。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN = 1e5 + 5;
const int INF = 0x3f3f3f3f;
string s,t;
int judge()
{
    int res=0
; for (int i=0;s[i];i++) res += s[i]<=t[i] ? t[i]-s[i] : 26-s[i]+t[i]; return res; } int main() { ios_base::sync_with_stdio(0); cin.tie(0); while (cin>>s>>t) { sort(s.begin(),s.end()); sort(t.begin(),t.end()); int ans=judge(); for
(int i=0;s[i];i++) if (s[i]>t[i]) { for (int j=i;s[j];j++) { t.push_back(t[i]); t.erase(i,1); ans=min(ans,judge()); } break; } cout<<ans<<endl; } return 0; }

C 最小生成樹 簽到題

#include<bits/stdc++.h>
using namespace std;
typedef long long LL; 
const int MAXN = 1e5 + 5;
const int INF = 0x3f3f3f3f;

int main()  
{
    ios_base::sync_with_stdio(0); cin.tie(0);
    int T;
    cin>>T;
    while (T--)
    {
        int n,x,minx=INF;
        LL sum=0;
        cin>>n;
        for (int i=1;i<=n;i++)
        {
            cin>>x;
            sum+=x;
            if (x<minx) minx=x;
        }
        if(n==1) cout<<0<<endl;
        else cout<<sum+1LL*minx*(n-2)<<endl;
    }
    return 0;
}

D 樹剖?然而我並不會。
應該資料比較水,我的樹上貪心複雜度能到O(4W*6W),按理說應該會超時,以後補樹剖吧。
注意題目中說明的 魔法要從葉子節點開始升級,一直升級到最頂層魔法(也就是到節點0)產生的能量才有效。
所以6節點升級兩個魔法到3,每個能產生4能量,3節點升級這兩個魔法到2,就變成每個魔法產生9能量(累加起來好計算),5節點升級一個魔法到2,產生3能量。
2節點只能升級兩個魔法,但是現在有三個,只能貪心選兩個產生能量多的嘍。

#include<bits/stdc++.h>
using namespace std;
#define mp make_pair
typedef long long LL; 
typedef pair<int,int> PII;
const int MAXN = 1e5 + 5;
const int INF = 0x3f3f3f3f;

vector<int> e[MAXN];
int num[MAXN],power[MAXN];
void dfs(int u, priority_queue<PII> &x)
{
    if (e[u].empty())
    {
        x.push(mp(power[u],num[u]));
        return;
    }
    priority_queue<PII> y;
    for (int v:e[u]) dfs(v,y);
    while (!y.empty() && num[u])
    {
        PII tmp = y.top(); y.pop();
        tmp.first+=power[u];
        if (tmp.second <= num[u]) num[u]-=tmp.second;
        else tmp.second=num[u],num[u]=0;
        x.push(tmp);
    }
}
int main()  
{
    ios_base::sync_with_stdio(0); cin.tie(0);

    int n;
    cin>>n;
    for (int i=1,x;i<=n;i++)
    {
        cin>>x>>num[i]>>power[i];
        e[x].push_back(i);
    }
    num[0]=INF;
    priority_queue<PII> x;
    dfs(0,x);
    LL ans=0;
    while (!x.empty())
    {
        ans+=1LL*x.top().first*x.top().second;
        x.pop();
    }
    cout<<ans<<endl;
    return 0;
}

E 比賽讀錯題意了,難受。存在j< i且a[j]< a[i],那麼a[i]是“good”的,刪掉一個數,讓“good”的數最多,數量相同的有多個,刪掉最小的那個。 統計刪掉a[i]會少多少個“good”的數就好了。維護數組裡兩個最小值,如果都比a[i]小,那麼a[i]一定是“good”,如果只有一個比a[i]小,刪掉小的那個a[i]就不是“good”了。

#include<bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
const int MAXN = 1e6 + 5;
int a[MAXN],num[MAXN];

int main()
{
    ios_base::sync_with_stdio(0); cin.tie(0);
    int T;
    cin>>T;
    while (T--)
    {
        memset(num,0,sizeof num);
        int n;
        cin>>n;
        for (int i=1,idx=1,x=INF,y=INF;i<=n;i++)
        {
            cin>>a[i];
            if (y<a[i]) num[i]++;
            else if (x<a[i]) y=a[i],num[i]++,num[idx]++;
            else y=x,x=a[i],idx=i;
        }
        int idx=1;
        for (int i=2;i<=n;i++)
        {
            if (num[idx]==num[i]&&a[i]<a[idx]) idx=i;
            if (num[idx]>num[i]) idx=i;
        }
        cout<<a[idx]<<endl;
    }
    return 0;
}

F 明顯容斥,不要問我為什麼寫的這麼麻煩。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MOD = 1e9 + 7;

int main()
{
    ios_base::sync_with_stdio(0); cin.tie(0);
    int T;
    cin>>T;
    while(T--)
    {
        LL l1,l2,l3,l4,r1,r2,r3,r4,ans;
        cin>>l1>>r1>>l2>>r2>>l3>>r3>>l4>>r4;
        //a,b,c,d對應四區間
        LL a=r1-l1+1,b=r2-l2+1,c=r3-l3+1,d=r4-l4+1;
        //ab對應前兩區間可能取相同元素個數,下同
        LL ab = max(min(r1,r2)-max(l1,l2)+1,0LL);
        LL bc = max(min(r2,r3)-max(l2,l3)+1,0LL);
        LL cd = max(min(r3,r4)-max(l3,l4)+1,0LL);
        LL ad = max(min(r1,r4)-max(l1,l4)+1,0LL);
        LL abc = max(min(min(r1,r2),r3)-max(max(l1,l2),l3)+1,0LL);
        LL abd = max(min(min(r1,r2),r4)-max(max(l1,l2),l4)+1,0LL);
        LL acd = max(min(min(r1,r3),r4)-max(max(l1,l3),l4)+1,0LL);
        LL bcd = max(min(min(r2,r3),r4)-max(max(l2,l3),l4)+1,0LL);
        LL abcd = max(min(min(r1,r2),min(r3,r4))-max(max(l1,l2),max(l3,l4))+1,0LL);

        ans = a*b%MOD*c%MOD*d%MOD;
        //減掉不滿足一個條件的
        ans -= ab*c%MOD*d%MOD; //a==b
        ans -= a*bc%MOD*d%MOD; //b==c
        ans -= a*b%MOD*cd%MOD; //c==d
        ans -= ad*b%MOD*c%MOD; //a==d
        //加上多減的滿足兩個條件的
        ans += abc*d%MOD; //a==b b==c
        ans += abd*c%MOD; //a==b a==d
        ans += acd*b%MOD; //a==d c==d
        ans += bcd*a%MOD; //b==c c==d
        ans += ab*cd%MOD; //a==b c==d
        ans += ad*bc%MOD; //a==d b==c
        //減掉多加的滿足四個條件的
        ans -= 3*abcd;
        cout<<(ans%MOD+MOD)%MOD<<endl;
    }
    return 0;
}

G NIM博弈+DP
NIM和為0時Bob能獲勝,也就是求去掉幾堆石子後,剩下的石子亦或值為零的個數。我們可以求去掉的這幾堆石子亦或值等於所有石子亦或值的個數。
dp陣列開二維, 一維是幾堆石子,一維是亦或值,裡面儲存符合條件的個數。
dp方程:dp[i+1][j^x] = (dp[i+1][j^x] + dp[i][j]) % MOD;,具體含義看程式碼吧。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MOD = 1e9 + 7;
LL dp[11][1111];
int main()
{
    ios_base::sync_with_stdio(0); cin.tie(0);

    int T;
    cin>>T;
    while (T--)
    {
        memset(dp,0,sizeof dp); dp[0][0]=1;
        int n,d,sum=0,x;
        cin>>n>>d;
        while(n--)
        {
            int x;
            cin>>x;
            sum^=x;
            for (int i=d-1;i;i--)
                for (int j=0;j<1024;j++)
                    dp[i+1][j^x] = (dp[i+1][j^x] + dp[i][j]) % MOD;
            dp[1][x]++;
        }
        LL ans=0;
        for (int i=0;i<=d;i++)
            ans = (ans+dp[i][sum])%MOD;
        cout<<ans<<endl;
    }
    return 0;
}

H題 %SDU大佬
找規律可發現一塊多米諾骨牌最多隻會動一次除非往反方向走,又因為骨牌動一下等於空格向上/下/左/右方向移動兩格,所以直接寬度優先搜尋空格可以到的位置有多少個就是有多少解

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;

int n,m,k;
int mmap[11][11111];
int dx[] = {-1,0,1,0};
int dy[] = {0,1,0,-1};
queue<PII> que;
int bfs()
{
    for (int i=1,f=1;i<=n&&f;i++)
        for (int j=1;j<=m&&f;j++)
            if(!mmap[i][j])
            {
                f=0;
                que.push(make_pair(i,j));
            }
    int ans=0;
    while (!que.empty())
    {
        int x=que.front().first, y=que.front().second; 
        que.pop();
        for (int i=0;i<4;i++)
            if (mmap[x+dx[i]][y+dy[i]]!=0 && mmap[x+dx[i]][y+dy[i]] == mmap[x+dx[i]*2][y+dy[i]*2])
            {
                ans++;
                que.push(make_pair(x+dx[i]*2,y+dy[i]*2));
            }
    }
    return ans;
}
int main()
{
    ios_base::sync_with_stdio(0); cin.tie(0);

    while(cin>>n>>m>>k)
    {
        memset(mmap,0,sizeof mmap);
        for (int i=1,a,b,c,d,cnt=1;i<=k;i++,cnt++)
        {
            cin>>a>>b>>c>>d;
            mmap[a][b]=cnt;
            mmap[c][d]=cnt;
        }
        cout<<bfs()<<endl;
    }
    return 0;
}