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;
}