cf 1556(div1+div2)
比賽連線:
半天才做出B,更半天才做出C——因為少考慮了一種情況,C還WA了兩次,最後才過的……
A
分析:
相當於兩邊同時\(+x\),然後一邊\(+k\)、一邊\(-k\)。稍微判斷一下就好了。
程式碼如下
#include
using namespace std;
int T,c,d;
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&c,&d);
if((c+d)&1){puts("-1"); continue;}
int x=(c+d)/2,ans=0;
if(x)ans++;
if(c==x&&d==x){printf("%d\n",ans); continue;}
if(x-c==d-x)ans++; else ans+=2;
printf("%d\n",ans);
}
return 0;
}
B
分析:
首先,最終狀態一種是奇數位置是奇數、偶數位置是偶數;一種是奇數位置是偶數、偶數位置是奇數。用這個判斷一下不合法情況就行了。然後兩種取優。
至於怎麼挪……一開始想了半天\(set\)、連結串列之類,為了維護挪了以後的序列。但是後來發現不用,因為每個數最終在的位置是確定的,比如奇數就是按它們的相對順序排列在奇數/偶數位置上(保持相對順序使操作次數最少)。所以按順序直接朝目標位置挪就行。
那麼怎麼考慮奇數和偶數挪的時候彼此的影響呢?實際上只考慮把奇數挪到合適位置上就行,挪完以後會發現偶數也自然在合適位置上了。
我們從前往後按順序挪,那會不會出現前面的一個奇數要挪到後面去,挪的過程中把後面的那個奇數挪前了,導致答案算錯而且不優?實際上也不會。因為這種可以看作是先挪了後面的、再挪前面的(因為它們都要挪到後面去,所以這樣更優)。
程式碼如下
#include
#define ll long long
using namespace std;
int const N=1e5+5;
ll const inf=1e12;
int T,n,a[N];
int ab(int x){return x<0?-x:x;}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d",&n); int num0=0,num1=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]); a[i]%=2;
if(!a[i])num0++; else num1++;
}
if(n&1)
{
if(ab(num0-num1)!=1){puts("-1"); continue;}
ll ans=0;
if(num0>num1)
{
for(int i=1,p0=1,p1=2;i<=n;i++)
{
if(!a[i])ans+=ab(i-p0),p0+=2;
// else ans+=(i-p1),p1+=2;
}
}
else
{
for(int i=1,p1=1,p0=2;i<=n;i++)
{
if(!a[i])ans+=ab(i-p0),p0+=2;
// else ans+=(i-p1),p1+=2;
}
}
printf("%lld\n",ans);
}
else
{
if(num0!=num1){puts("-1"); continue;}
ll a1=0,a2=0;
for(int i=1,p0=1,p1=2;i<=n;i++)
{
if(!a[i])a1+=ab(i-p0),p0+=2;
// else a1+=(i-p1),p1+=2;
}
for(int i=1,p1=1,p0=2;i<=n;i++)
{
if(!a[i])a2+=ab(i-p0),p0+=2;
// else a2+=(i-p1),p1+=2;
}
printf("%lld\n",min(a1,a2));
}
}
return 0;
}
C
分析:
這個也想了好久……我們兩個兩個列舉,也就是當前\(c_i\)是一群左括號,\(c_{i+1}\)是一群右括號。然後我們用一個棧記錄兩個值:\(rem[i]\)和\(pre[i]\),分別表示前面剩下的左括號,以及那個左括號後面跟了多少個合法的最小括號序列。下面我們考慮每個合法子串的右端點。
若\(c_i>c_{i+1}\),說明當前左括號多於右括號,那麼\(ans += c_{i+1}\),而\(c_{i+1}\)這些右括號作為右端點也再不能往前走了。棧增加一個元素,\(res[i] = c_i - c_{i+1}, pre[i]=1\)。
若\(c_i<c_{i+1}\),說明當前右括號多於左括號,那麼首先$ans += c_i, c_{i+1} -= c_i \(,然後右括號繼續往前走,也就是提取棧裡的元素,每次\)ans += pre[i], ans += rem[i], c_{i+1} -= rem[i]\(,直到棧空了或者\)c_{i+1} \leq rem[i]\(。
如果是\)c_{i+1} < rem[i]\(,說明右括號在這裡用完了,那麼\)ans += c_{i+1}, rem[i] -= c_{i+1}\(,然後\)pre[i]=1\(,表示當前最右的括號與棧內此元素的左括號形成了一個合法子串。
如果是\)c_{i+1} = rem[i]\(,那麼左右括號在這裡恰好匹配完了。對應操作一番即可。這裡要注意!!匹配完以後\)ans\(還要加上棧內前一個元素的\)pre\(,表示匹配後的子串還可以連上那些合法子串計入答案!!一開始寫的時候沒注意這個,調了一小時。
若\)c_i=c_{i+1}\(,和上面類似。
因為每個位置至多入棧出棧一次,所以時間複雜度是\)O(n)$的。
程式碼如下
#include
#define ll long long
using namespace std;
int const N=1005;
int n,c[N],cnt;
ll ans,rem[N],pre[N];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&c[i]);
for(int i=1;i<=n;i+=2)
{
if(i==n)continue;//!
if(c[i]>c[i+1])
{
ans+=c[i+1];
rem[++cnt]=c[i]-c[i+1]; pre[cnt]=1;
}
else if(c[i]==c[i+1])
{
ans+=c[i];
if(cnt)ans+=pre[cnt],pre[cnt]++;//rem[cnt]=0 only at head
else rem[++cnt]=0,pre[cnt]=1;
}
else
{
ans+=c[i]; int rt=c[i+1]-c[i];
while(cnt&&rt>rem[cnt])//rt!=0
{
ans+=pre[cnt]; ans+=rem[cnt];
rt-=rem[cnt]; cnt--;
}
if(cnt)
{
/*
if(rt==0)//! rt=0&&rem=0
{
ans+=pre[cnt]; pre[cnt]++;
continue;
}
*/
if(rt==rem[cnt])
{
ans+=pre[cnt]; ans+=rem[cnt];
cnt--;
if(cnt)
{
ans+=pre[cnt];//!!
pre[cnt]++;
}
else rem[++cnt]=0,pre[cnt]=1;
}
else
{
ans+=pre[cnt]; ans+=rt;
rem[cnt]-=rt; pre[cnt]=1;
}
}
}
// printf("i=%d ans=%lld\n",i,ans);
}
printf("%lld\n",ans);
return 0;
}
D
分析:
又忘記了那個重要的式子:\((x or y) + (x and y) = (x+y)\)
所以可以問六次得到前三個數的值;知道一個數的值以後就可以\(2n\)次詢問把後面\(n\)個數都得到。然後排序即可。
如此簡單粗暴……
程式碼如下
#include
#include
#define ll long long
using namespace std;
int const N=1e4+5;
int n,k,a[N];
int main()
{
scanf("%d%d",&n,&k);
ll s1,s2,s3,sum,x,y;
puts("or 1 2"); fflush(stdout);
scanf("%lld",&x);
puts("and 1 2"); fflush(stdout);
scanf("%lld",&y);
s1=x+y;
puts("or 2 3"); fflush(stdout);
scanf("%lld",&x);
puts("and 2 3"); fflush(stdout);
scanf("%lld",&y);
s2=x+y;
puts("or 1 3"); fflush(stdout);
scanf("%lld",&x);
puts("and 1 3"); fflush(stdout);
scanf("%lld",&y);
s3=x+y;
sum=(s1+s2+s3)/2;
a[1]=sum-s2; a[2]=sum-s3; a[3]=sum-s1;
for(int i=4;i<=n;i++)
{
printf("or 1 %d\n",i); fflush(stdout);
scanf("%lld",&x);
printf("and 1 %d\n",i); fflush(stdout);
scanf("%lld",&y);
a[i]=x+y-a[1];
}
sort(a+1,a+n+1);
printf("finish %d\n",a[k]);
return 0;
}