Codeforces Global Round 19
A
阅读理解题,转化一下就发现除了排好序的数组,别的都有可能无序。
$\texttt{Code}$
#include<bits/stdc++.h>
#define int long long
#define inf (1<<30)
#define INF (1ll<<60)
#define pb push_back
#define pii pair<int,int>
#define mkp make_pair
#define fi first
#define se second
#define rep(i,j,k) for(int i=(j);i<=(k);i++)
#define per(i,j,k) for(int i=(j);i>=(k);i--)
using namespace std;
const int MAXN=1e4+10;
int a[MAXN],b[MAXN];
void solve(){
int n;scanf("%lld",&n);
rep(i,1,n) scanf("%lld",&a[i]),b[i]=a[i];
sort(a+1,a+1+n);
bool ok=1;
rep(i,1,n) if(a[i]!=b[i]){ok=0;break;}
puts(ok?"NO":"YES");
}
signed main()
{
int T;
for(scanf("%lld",&T);T--;)
solve();
return 0;
}
B
阅读理解题,我们发现要使得最终 \(\sum{mex}+cnt\) 尽可能大,然后我们发现,每一段的贡献最大最大也只有 \(len+1\),而如果我们把这一段裂成 \(len\) 个长度为 \(1\) 的区间,其贡献必然不劣于整段的最优贡献。所以每一段的长度都是 \(1\),暴力统计答案即可。
$\texttt{Code}$
#include<bits/stdc++.h>
#define ll long long
#define inf (1<<30)
#define INF (1ll<<60)
#define pb push_back
#define pii pair<int,int>
#define mkp make_pair
#define fi first
#define se second
#define rep(i,j,k) for(int i=(j);i<=(k);i++)
#define per(i,j,k) for(int i=(j);i>=(k);i--)
using namespace std;
const int MAXN=110;
ll a[MAXN];
void solve(){
int n;scanf("%d",&n);
rep(i,1,n) scanf("%lld",&a[i]);
ll ans=0;
rep(i,1,n){
rep(j,i,n){
ll cur=j-i+1;
rep(k,i,j){
if(a[k]==0) cur++;
}ans+=cur;
}
}printf("%lld\n",ans);
}
int main()
{
int T;
for(scanf("%d",&T);T--;)
solve();
return 0;
}
C
猜结论题,显然第一个和最后一个位置是迷惑你的。然后考虑把中间的放到两边去。容易想到最优方案是直接把每个位置拿出两个,然后放到第一个和最后一个。这样如果中间的和为 \(sum\),最终答案就是 \(\frac{sum}{2}\)。然后问题是有奇数,这样干可能会导致拿不完。那么考虑多花费一次把奇数变成偶数。最简单的构造就是,如果刨去偶数,那么剩下的奇数相邻的两两配对,每一对中前一个把自己的一个扔给后一个,这样两个都变成偶数个了。如果配对之后多出一个奇数,那么掏出一个已经是偶数的数,然后把自己的一个给它,这样容易发现,每个奇数都相当于使总和加一。于是答案就是 \(\frac{sum+odd}{2}\),注意特判 \(n=3\) 和中间全是 \(1\) 的情况。
$\texttt{Code}$
#include<bits/stdc++.h>
#define ll long long
#define inf (1<<30)
#define INF (1ll<<60)
#define pb push_back
#define pii pair<int,int>
#define mkp make_pair
#define fi first
#define se second
#define rep(i,j,k) for(int i=(j);i<=(k);i++)
#define per(i,j,k) for(int i=(j);i>=(k);i--)
using namespace std;
const int MAXN=1e5+10;
ll a[MAXN];
void solve(){
int n;scanf("%d",&n);
rep(i,1,n) scanf("%lld",&a[i]);
if(n==3){
if(a[2]&1) puts("-1");
else printf("%lld\n",a[2]/2);
return;
}
ll odd=0,ans=0;bool ok=0;
rep(i,2,n-1) ans+=a[i];
rep(i,2,n-1) if(a[i]&1) odd++;
rep(i,2,n-1) if(a[i]>1) ok=1;
if(!ok) puts("-1");
else printf("%lld\n",(ans+odd)/2);
}
int main()
{
int T;
for(scanf("%d",&T);T--;)
solve();
return 0;
}
D
套路题,考虑两组达到什么目的的时候会使得答案最小。首先我们把答案的贡献化简一下,平方项是无关于答案的(无论怎么交换都不变),那么只有每一组内两两元素的乘积。有个经典结论就是,当两组和差值最小的时候,这个每组内乘积的和是最小的。
证明:设两组元素的总和是 \(sum\),其中一组是 \(i\),那么最终这个两两元素的乘积去除无关的项之后是 \(i^2+(sum-i)^2=2\times i^2-2\times sum\times i+sum^2\),那这个东西的最小值显然是 \(i=\frac{sum}{2}\),根据二次函数的单调性可以得出越接近它的,最终答案就越小。
然后对两组统计通过交换能使某一组的和变成多少,用个 \(dp\) 转移一下。最后枚举其中一组的和 \(i\),那么两组的差值就是 \(\operatorname{abs}(sum-2\times i)\)。取出最小的差值,那么其中一组的贡献就是 \(i^2\),另一组就是 \((sum-i)^2\),然后计算平方项,刨去前面算过的平方项,每个元素还有 \(n-2\) 个。
$\texttt{Code}$
#include<bits/stdc++.h>
#define ll long long
#define inf (1<<30)
#define INF (1ll<<60)
#define pb push_back
#define pii pair<int,int>
#define mkp make_pair
#define fi first
#define se second
#define rep(i,j,k) for(int i=(j);i<=(k);i++)
#define per(i,j,k) for(int i=(j);i>=(k);i--)
using namespace std;
const int MAXN=110;
const int MAXM=2e4+10;
int a[MAXN],b[MAXN];
bool dp[MAXN][MAXM];
void solve(){
int n;scanf("%d",&n);
int sum=0,sqr=0;
rep(i,1,n) scanf("%d",&a[i]),sum+=a[i],sqr+=a[i]*a[i];
rep(i,1,n) scanf("%d",&b[i]),sum+=b[i],sqr+=b[i]*b[i];
rep(i,0,n) rep(j,0,sum) dp[i][j]=0;
dp[0][0]=1;
rep(i,1,n){
rep(j,a[i],sum) dp[i][j]|=dp[i-1][j-a[i]];
rep(j,b[i],sum) dp[i][j]|=dp[i-1][j-b[i]];
}int mn=inf,ans=0;
rep(i,0,sum){
if(!dp[n][i]) continue;
if(mn>abs(2*i-sum))
mn=abs(2*i-sum),ans=i;
}
printf("%d\n",(sum-ans)*(sum-ans)+ans*ans+(n-2)*sqr);
}
int main()
{
int T;
for(scanf("%d",&T);T--;)
solve();
return 0;
}
E
经典题+STL 大法。这题场上没写完并且好像 fst 了一车人。首先一个经典的套路就是,对于任意一个数组,我们把其中的元素按照出现次数分类,那么类数是不超过 \(\sqrt{n}\) 的。很好证明,考虑最劣情况就是 \(\sum_{i=1}^x\le n\) 的最大解。然后我们暴力枚举任意两类,计算答案。这样复杂度乘上一个 \(n\)。
然后就是处理坏对的问题了。其实也很暴力,首先对两类分别从大到小排序,然后对每一类弄一个指针指向其第一个元素,把头上两个一对加入到 set 或者 priority_queue 中,每次取出和最大的一对,判断是不是坏对,如果是,那么把这一对删掉然后把第一类中的当前元素与第二类中的当前元素后一个为一对加入堆或者 set 中,同时,把第一类后一位和第二类当前的加入。直到取出最大的不是坏对。
upd:不能用堆,否则复杂度会有问题,建议使用 set,自动去重,能够保证复杂度。
$\texttt{Code}$
#include<bits/stdc++.h>
#define int long long
#define inf (1<<30)
#define INF (1ll<<60)
#define pb push_back
#define pii pair<int,int>
#define mkp make_pair
#define fi first
#define se second
#define rep(i,j,k) for(int i=(j);i<=(k);i++)
#define per(i,j,k) for(int i=(j);i>=(k);i--)
using namespace std;
const int MAXN=1e6+10;
struct node{
int val,xt,yt;node(){}
node(int _val,int _xt,int _yt){val=_val,xt=_xt,yt=_yt;}
bool friend operator<(node a,node b){return a.val==b.val?(a.xt==b.xt?a.yt<b.yt:a.xt<b.xt):a.val>b.val;}
};
map<int,int> cnt;
map<pii,bool> ban;
vector<int> cls[MAXN],ext;
int calc(vector<int> &va,vector<int> &vb){
set<node> st;st.insert(node(va[0]+vb[0],0,0));
while(st.size()){
auto it=*st.begin();st.erase(it);
if(ban[mkp(va[it.xt],vb[it.yt])]||va[it.xt]==vb[it.yt]){
if(it.xt+1<va.size())
st.insert(node(va[it.xt+1]+vb[it.yt],it.xt+1,it.yt));
if(it.yt+1<vb.size())
st.insert(node(va[it.xt]+vb[it.yt+1],it.xt,it.yt+1));
}else return it.val;
}return 0;
}
bool cmp(int x,int y){return x>y;}
void solve(){
int n,m,a,b;scanf("%lld%lld",&n,&m);
rep(i,1,n) scanf("%lld",&a),cnt[a]++;
rep(i,1,m) scanf("%lld%lld",&a,&b),ban[mkp(a,b)]=ban[mkp(b,a)]=1;
for(auto s:cnt) cls[s.se].pb(s.fi);
rep(i,1,n) if(cls[i].size())
ext.pb(i),sort(cls[i].begin(),cls[i].end(),cmp);
int ans=0;
rep(i,0,ext.size()-1)
rep(j,i,ext.size()-1)
ans=max(ans,(ext[i]+ext[j])*calc(cls[ext[i]],cls[ext[j]]));
printf("%lld\n",ans);
cnt.clear();ban.clear();ext.clear();
rep(i,0,n) cls[i].clear();
}
signed main()
{
int T;
for(scanf("%lld",&T);T--;)
solve();
return 0;
}
upd:关于为什么调那么久:set 在重载的时候一定要每一个关键字都比较过去,否则会被 set 认为是相等然后去重……(并膜拜 \(\texttt{c}\color{red}{\texttt{mll02}}\))
F
没看
G
没看
H
没看