題目鏈接:
A.Candies
B.Balanced Array
C.Alternating Subsequence
D.Constant Palindrome Sum
E.Weights Distributing
F.Restore the Permutation by Sorted Segments
- A.Candies
思路:簡單枚舉即可
AC代碼:
void solve() {
cin>>n;
for(ll i=3,k=4;;i+=k,k*=2) {
if(n%i==0) {
cout<<n/i<<'\n';
return ;
}
}
}
- B.Balanced Array
思路:簡單模擬
AC代碼:
void solve(ll sum=0) {
cin>>n;
if(((n+1)/2)&1) {
cout<<"NO\n";
return ;
}
cout<<"YES\n";
rep(i,n/2) {
cout<<i*2<<' ';
sum+=i*2;
}
rep(i,(n+1)/2-1) {
cout<<i*2-1<<' ';
sum-=i*2-1;
}
cout<<sum<<'\n';
}
- C.Alternating Subsequence
思路:貪心,連續相同符號的值取最大。
AC代碼:
void solve(ll sum=0) {
cin>>n;
ll x,y=0;
rep(i,n) {
cin>>x;
if(x*y>0) y=max(x,y);
else sum+=y,y=x;
}
cout<<sum+y<<'\n';
}
- D.Constant Palindrome Sum
思路:對於每一對值,存在一個範圍,最終的x如果在這個範圍裏,那麼對於這對值只需要修改一個數或不需要修改,反之兩個數都需要修改。
這個範圍D爲:[min(x,y)+1,max(x,y)+k]
我們需要得到對於每一個可能的x,其在多少對元素的範圍D內,所以可以想到區間修改,單點查詢的解法。
因爲是離線的,所以用差分數組+前綴和即可實現,我這裏用了樹狀數組實現。
AC代碼:
void solve(ll ans=0x3f3f3f3f) {
cin>>n>>k;
rep(i,2*k) sum[i]=c[i]=0;
rep(i,n/2) cin>>a[n/2-i+1];
rep(i,n/2) {
cin>>x;
int l=min(x,a[i])+1;
int r=max(x,a[i])+k;
sum[x+a[i]]++;
for(int j=l;j<=2*k;j+=j&-j) c[j]++;
for(int j=r+1;j<=2*k;j+=j&-j) c[j]--;
}
rep(i,2*k) {
ll t=0;
for(int j=i;j;j-=j&-j) t+=c[j];
ans=min(ans,n-t-sum[i]);
}
cout<<ans<<'\n';
}
- E.Weights Distributing
思路:預處理出每個點到a,b,c三點的距離(邊數),可以想到一個好的方案一定是a到b和b到c間包含儘可能多的重複,在此基礎上其餘邊儘可能少。
可以貪心的認爲所有的重複路徑一定與b點直接相連(i.e.最終方案一定是這樣的:a走過幾條邊到某點d,經過重複利用的邊集v進入b,再走過v到d,最後走到c)於是我們只需要枚舉每個d點計算出max{dst(a to d)+dst(d to b)*2+dst(d to c)}即可。
AC代碼:
const int N=4e5+5;
ll n,m,k,x,y,U,V,W;
ll sum[N],a[N],dst[4][N];
vector<int> ve[N];
void bfs(int V,int t) {
queue<int> que;
que.push(V),dst[t][V]=0;
while(!que.empty()) {
int u=que.front();
que.pop();
for(int v:ve[u]) if(dst[t][v]==-1) {
dst[t][v]=dst[t][u]+1;
que.push(v);
}
}
}
void solve(ll ans=1e18) {
cin>>n>>m>>U>>V>>W;
rep(j,3) rep(i,n) dst[j][i]=-1;
rep(i,n) ve[i].clear();
rep(i,m) cin>>a[i];
sort(a+1,a+m+1);
rep(i,m) sum[i]=sum[i-1]+a[i];
rep(i,m) {
cin>>x>>y;
ve[x].push_back(y);
ve[y].push_back(x);
}
bfs(V,1),bfs(U,2),bfs(W,3);
rep(i,n) {
ll d1=dst[1][i],d2=dst[2][i],d3=dst[3][i];
if(d1+d2+d3>m) continue;
ans=min(ans,sum[d1+d2+d3]+sum[d1]);
}
cout<<ans<<'\n';
}
- F.Restore the Permutation by Sorted Segments
思路:枚舉待求排列的第一位數,即可推出後續所有元素。
具體實現:將每對l~r間所有元素存到集合中。每次刪除每個集合中當前推出的元素,刪除後哪個集合只剩下一個元素,這個元素就一定是原數組的下一個元素,如果出現多個或沒有這樣的元素,則說明方案不成立。
從思路上說其實比E更簡單一些,只是代碼寫的稍有些亂。
AC代碼:
bool check(int u) {
ans[1]=u;
rep(i,n) vis[i]=0;
for(int v:ve[u]) vis[v]=1;
rep(i,n-1) {
int cnt=0,t;
for(int v:ve[u]) {
b[v].erase(u);
if(b[v].size()==1) cnt++,t=v;
}
if(cnt^1) return false;
if(vis[t]&&num[t]!=i+1) return false;
u=ans[i+1]=*b[t].begin();
}
rep(i,n) {
if(i-1) cout<<' ';
cout<<ans[i];
}
cout<<endl;
return true;
}
void solve() {
cin>>n;
rep(i,n) ve[i].clear(),a[i].clear(),b[i].clear();
rep(i,n-1) {
cin>>num[i];
rep(j,num[i]) {
cin>>x;
ve[x].push_back(i);
a[i].insert(x);
}
}
rep(i,n) {
rep(j,n) b[j]=a[j];
if(check(i)) return ;
}
}