Codeforces Round #639 (Div. 2)
前情提要
这个博客好像已经停更两个月了呢。
这段时间就算是摸鱼了吧...
在两周前刚刚比完校赛,我们队终于以rank#8,rank#9,rank#10的成绩获得了暑期集训的资格。
队名也由之前随便起的Avengers,Prevengers改成了Die_Java
队伍的wiki也搬家搬到了这里
这段时间,知识点该不会啥还是不会啥,但是每周日的训练赛还是打的。
总之爷又回来了!!!
A. Puzzle Pieces
题解
全摆一行显然可以,一横一竖这么交替摆就行,考虑两行的话,那个很讨厌的凹槽画画图就能发现,只有2*2才能满足,除了这两类剩下的都不行。
#include<bits/stdc++.h>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
typedef long long LL;
typedef pair<int,int> PII;
#define X first
#define Y second
inline int read()
{
int x=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){x=x*10+c-'0';c=getchar();}
return x*f;
}
int T,n,m;
int main()
{
T=read();
while(T--)
{
n=read();m=read();
if(n>m)swap(n,m);
if(n==1)printf("YES\n");
else if(n==2 && m==2)printf("YES\n");
else printf("NO\n");
}
return 0;
}
B. Card Constructions
题解
先来一个小学级别的递推:\(f_i=f_{i-1}+3*i-1\),然后就根据题意不断二分就好了
#include<bits/stdc++.h>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
typedef long long LL;
typedef pair<int,int> PII;
#define X first
#define Y second
inline int read()
{
int x=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){x=x*10+c-'0';c=getchar();}
return x*f;
}
int T,n;
LL f[50010];
int func(int x)
{
if(x<=0)return 0;
int pos=lower_bound(f+1,f+50000+1,x)-f;
if(f[pos]==x)return 1;
if(f[pos-1]==0)return 0;
return 1+func(x-f[pos-1]);
}
int main()
{
f[1]=2;
for(int i=2;i<=50000;i++)f[i]=f[i-1]+3ll*i-1ll;
T=read();
while(T--)
{
n=read();
printf("%d\n",func(n));
}
return 0;
}
C. Hilbert's Hotel
题解
经典希尔伯特酒店。(当时没发现这个标题是这个意思,导致读题卡了半天)
有冲突的意思,翻译一下就是\(a_i-a_j \equiv i-j(mod N)\),进一步写\(a_i-a_j=k*N+i-j\),移项\(a_i+i \equiv a_j+j (mod N)\),开个vis数组记录一下即可,注意要把负数给加成正数。
#include<bits/stdc++.h>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
typedef long long LL;
typedef pair<int,int> PII;
#define X first
#define Y second
inline int read()
{
int x=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){x=x*10+c-'0';c=getchar();}
return x*f;
}
const int maxn=200010;
int T,n,a;
bool vis[maxn];
int main()
{
T=read();
while(T--)
{
n=read();
bool ok=0;
for(int i=0;i<n;i++)vis[i]=0;
for(int i=1;i<=n;i++)
{
a=(read()+i+(1000000000)/n*n+n)%n;
if(vis[a]==1 && !ok){
printf("NO\n");
ok=1;
}
vis[a]=1;
}
if(!ok)printf("YES\n");
}
return 0;
}
D. Monopole Magnets
题解
结论题
最终答案就是四连块数量。
判无解的情况多点儿,
第一点是:黑色块不能有凹进去的部分,因为如果有凹进去部分的话一定会被吸到白色区域。
第二点,在本行本列都没有黑格的地方是可以放北极磁铁的,因为不会有影响,其他地方北极磁铁必须放到黑格。
代码可读性极差,自己都不知道自己在写啥
#include<bits/stdc++.h>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
typedef long long LL;
typedef pair<int,int> PII;
#define X first
#define Y second
inline int read()
{
int x=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){x=x*10+c-'0';c=getchar();}
return x*f;
}
const int maxn=1010;
int n,m,ans,dx[4]={1,0,-1,0},dy[4]={0,1,0,-1};
char pic[maxn][maxn];
bool vis[maxn][maxn],heng[maxn],shu[maxn];
bool legal(int x,int y){return x>=0 && x<n && y>=0 && y<m;}
void dfs(int x,int y,int col)
{
vis[x][y]=1;
for(int i=0;i<4;i++)
{
int nx=x+dx[i],ny=y+dy[i];
if(legal(nx,ny) && pic[nx][ny]=='#' && !vis[nx][ny])dfs(nx,ny,col);
}
}
int main()
{
n=read();m=read();
for(int i=0;i<n;i++)scanf("%s",pic[i]);
bool ass=0;
for(int i=0;i<n;i++)for(int j=0;j<m;j++)if(pic[i][j]=='#')ass=1;
if(!ass)return puts("0"),0;
for(int i=0;i<n;i++)
{
int ok=0;
for(int j=0;j<m;j++)if(pic[i][j]=='#')ok=1;
if(!ok)heng[i]=1;
}
for(int i=0;i<m;i++)
{
int ok=0;
for(int j=0;j<n;j++)if(pic[j][i]=='#')ok=1;
if(!ok)shu[i]=1;
}
for(int i=0;i<n;i++)for(int j=0;j<m;j++)if(heng[i] && shu[j])vis[i][j]=1;//,printf("%d %d\n",i,j);
for(int i=0;i<n;i++)
{
int ok=0,ok2=0;
for(int j=0;j<m;j++)
{
if(vis[i][j])ok2=1;
if(pic[i][j]=='#')
{
ok++;
while(j<m && pic[i][j]=='#')j++;
}
}
if(ok>1 || (!ok2 && !ok))return puts("-1"),0;
}
for(int i=0;i<m;i++)
{
int ok=0,ok2=0;
for(int j=0;j<n;j++)
{
if(vis[j][i])ok2=1;
if(pic[j][i]=='#')
{
ok++;
while(j<n && pic[j][i]=='#')j++;
}
}
if(ok>1 || (!ok2 && !ok))return puts("-1"),0;
}
for(int i=0;i<n;i++)for(int j=0;j<m;j++)if(pic[i][j]=='#' && !vis[i][j])dfs(i,j,++ans);
printf("%d\n",ans);
return 0;
}
E. Quantifier Question
题解
没想明白,下次一定。
F. Résumé Review
题解
本人有两种理解。
第一种理解,我称之为离散拉格朗日数乘法。
先说目标函数:
\(f(b_1,...,b_n)=\sum^{n}_{i=1} b_i*(a_i-b_i^2)\)
然后是约束条件::
\(\sum b_i=k\)
\(0 \leq b_i \leq a_i\) 有\(n\) 组。
讲道理,这种约束条件带有不等式的应该由一种叫KKT条件的东西来解决,
但是题目中已经说了都是离散的,所以我们先试试求一求目标函数的离散偏导
$\frac{\part f}{\part b_i} $ = $ b_i(a_i-b_i^2) - (b_i-1)(a_i-(b_i-1)^2) =a_i-3b_i^2+3b_i+1 $ ,显然是一个关于\(b_i\) 的二次函数,其中\(0 \leq b_i \leq a_i\)。很巧的是,这个二次函数的对称轴为\(x=\frac{1}{2}\) ,在\(x=1,2,3\) 这种离散取值中完全是单调递减的,所以为了让目标函数最大,这些\(b_i\) 显然应该更靠左更好,只需要满足\(\sum b_i=k\)即可,所以我们就可以完全无视这\(n\) 个约束条件:\(0 \leq b_i \leq a_i\)。剩下的就套用正常拉格朗日数乘的规则,使得目标函数的梯度\(\nabla = (\frac{\part f}{\part b_1} ,...,\frac{\part f}{\part b_n})\)与约束条件函数的梯度\((1,..,1)\)平行,当然了这个梯度是离散梯度。
那么我们二分这个两个梯度向量的比值,分别求出每个\(b_i\) ,看他们是否和\(k\) ,直到满足题意为止。
当然了,二分即便到了最后也有大概率可能会出现\(\sum b_i>K\) 的情况,我们需要对\(b_i\)进行一些微调,用优先队列维护\(min(\frac{\part f}{\part b_i}(ans_i))\) ,表示这个\(ans\)减一后损失的贡献。执行\(\sum b_i-K\) 即可。
第二种理解,是比较正常的理解。
也是发现了类似于离散偏导单调递减的性质,那么每次指定一个\(i\) ,使得\(i\) 向右移动一格,会使最终答案少一点,我们就让那个减少值每次都最小,这样执行\(k\) 次一定是最大值。(就相当于在\(n\) 维空间走\(k\) 步),后面的操作请参考这里
复杂度是\(O(nlog(max{a_i})^2)\),之前我不想要第二个\(log\),就直接求根公式求得每个\(b_i\),结果一直错,后来发现是在计算\(\delta\) 的时候爆long long了。。。。其实可以用int_128解决,但是我最后还是认怂写了个二分了。
#include<bits/stdc++.h>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
typedef long long LL;
typedef pair<LL,LL> PII;
#define X first
#define Y second
inline LL read()
{
LL x=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){x=x*10ll+c-'0';c=getchar();}
return x*f;
}
const int maxn=100010;
LL F(LL x,LL y){return x-3ll*y*y+3ll*y-1;}
LL n,k,a[maxn],L=9e18,R=-9e18,ans[maxn],pos[maxn],minn=9e18;
priority_queue<PII,vector<PII>,greater<PII> > Q;
bool judge(LL index)
{
LL sum=0;
for(LL i=1;i<=n;i++)
{
LL left=0,right=a[i]+1;
while(right-left>1)
{
LL mid=left+right>>1ll;
if(F(a[i],mid)>=index)left=mid;
else right=mid;
}
if(F(a[i],left)>=index)pos[i]=left;
else pos[i]=right;
sum+=pos[i];
}
if(sum-k<minn && sum>=k)
{
minn=sum-k;
for(LL i=1;i<=n;i++)ans[i]=pos[i];
}
return sum<k;
}
int main()
{
n=read();k=read();
for(LL i=1;i<=n;i++)a[i]=read(),L=min(L,F(a[i],a[i])),R=max(R,a[i]);
while(R-L>1)
{
LL mid=L+R>>1ll;
if(judge(mid))R=mid;
else L=mid;
}
judge(L);judge(R);
for(int i=1;i<=n;i++)if(ans[i])Q.push(make_pair(F(a[i],ans[i]),i));
while(minn--)
{
PII now=Q.top();Q.pop();
ans[now.Y]--;
if(ans[now.Y])Q.push(make_pair(F(a[now.Y],ans[now.Y]),now.Y));
}
for(int i=1;i<n;i++)printf("%lld ",ans[i]);
printf("%lld\n",ans[n]);
return 0;
}