A - Shik and Stone
只要判断一下’#'的数量即可,如果只向右和下走的话,走过的格子数量一定是n+m−1n+m-1n+m−1个。
#include<bits/stdc++.h>
using namespace std;
#define RI register int
int n,m,js;char ch[10];
int main()
{
scanf("%d%d",&n,&m);
for(RI i=1;i<=n;++i) {
scanf("%s",ch+1);
for(RI j=1;j<=m;++j) js+=(ch[j]=='#');
}
if(js==n+m-1) puts("Possible");
else puts("Impossible");
return 0;
}
B - Construct Sequences
先将a和b构造成两个等差数列,公差ddd比较大(至少大于nnn吧),假设其中最大项是sss(显然s=1+(n−1)ds=1+(n-1)ds=1+(n−1)d)。
而ai+bi=(1+(i−1)d)+(1+(n−i+1)d)=2+(n−1)da_i+b_i=(1+(i-1)d)+(1+(n-i+1)d)=2+(n-1)dai+bi=(1+(i−1)d)+(1+(n−i+1)d)=2+(n−1)d
我们只要让apia_{p_i}api增加i−1i-1i−1,就可以让api+bpi=s+ia_{p_i}+b_{p_i}=s+iapi+bpi=s+i了。
而由于公差大于nnn,所以这么增加还是能让序列a满足条件。
#include<bits/stdc++.h>
using namespace std;
#define RI register int
int read() {
int q=0;char ch=' ';
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') q=q*10+ch-'0',ch=getchar();
return q;
}
const int N=100005;
int n,a[N],b[N];
int main()
{
int x;
n=read();
for(RI i=1;i<=n;++i) a[i]=b[n-i+1]=1+(i-1)*(n+1);
for(RI i=1;i<=n;++i) x=read(),a[x]+=i-1;
for(RI i=1;i<=n;++i) printf("%d ",a[i]);
puts("");
for(RI i=1;i<=n;++i) printf("%d ",b[i]);
puts("");
return 0;
}
C - Pushing Balls
我们来到操场的沙坑上,赶走在练习跳远的人,然后挖四个坑,然后找体育组借三个实心球放在四个坑直接,那么这些物品两两的初始距离是:
ddd,d+xd+xd+x,d+2xd+2xd+2x,d+3xd+3xd+3x,d+4xd+4xd+4x,d+5xd+5xd+5x
考虑一下不同的推球方法,当一个球进入一个坑后,就默不作声地用沙子将这个球埋起来,假装什么都没有发生过,将这些球重新编号。你会发现物品两两间距离有这么几种可能(表格头的编号是重编过的):
情况 | 坑1与球1 | 球1与坑2 | 坑2与球2 | 球2与坑3 |
---|---|---|---|---|
球1入坑1 | d+2xd+2xd+2x | d+3xd+3xd+3x | d+4xd+4xd+4x | d+5xd+5xd+5x |
球1入坑2 | 3d+3x3d+3x3d+3x | d+3xd+3xd+3x | d+4xd+4xd+4x | d+5xd+5xd+5x |
球2入坑2 | ddd | 3d+6x3d+6x3d+6x | d+4xd+4xd+4x | d+5xd+5xd+5x |
球2入坑3 | ddd | d+xd+xd+x | 3d+9x3d+9x3d+9x | d+5xd+5xd+5x |
球3入坑3 | ddd | d+xd+xd+x | d+2xd+2xd+2x | 3d+12x3d+12x3d+12x |
球3入坑4 | ddd | d+xd+xd+x | d+2xd+2xd+2x | d+3xd+3xd+3x |
期望 | 8d+5x6\frac{8d+5x}{6}68d+5x | 8d+15x6\frac{8d+15x}{6}68d+15x | 8d+25x6\frac{8d+25x}{6}68d+25x | 8d+35x6\frac{8d+35x}{6}68d+35x |
哇,好神奇啊,期望的物品间距离居然还是个等差数列~
你会对这个发现感到异常惊奇,但还是不要忘了把实心球挖出来还回去。
现在来观察,等差数列的第一项就是我们表格的坑1与球1这一列,你会发现不管初始用几个球,这一列只有前两行是d+2xd+2xd+2x和3d+3x3d+3x3d+3x,其他行都是ddd。所以d′=d+2d+5x2nd'=d+\frac{2d+5x}{2n}d′=d+2n2d+5x。至于公差,我们观察一下球2与坑2这一列,发现这一列的期望永远会是d+x+2d+9x2nd+x+\frac{2d+9x}{2n}d+x+2n2d+9x,所以公差x′=x+4x2nx'=x+\frac{4x}{2n}x′=x+2n4x。
这样我们做nnn次对ddd和xxx(其实还有nnn别忘了)的变换,即可得到答案。
oh,对,至于每种局面下的推球期望距离,应该是d+2n−12xd+\frac{2n-1}{2x}d+2x2n−1,具体为什么自己推(zhǎo)一(guī)推(lǜ)吧。
#include<bits/stdc++.h>
using namespace std;
#define RI register int
typedef double db;
int n;db d,x,ans;
int main()
{
scanf("%d%lf%lf",&n,&d,&x);
db nn=n+n;
for(RI i=n;i>=1;--i) {
ans+=d+(nn-1)/2*x;
db dd=d+(2*d+5*x)/nn;
db xx=x+4*x/nn;
x=xx,d=dd,nn-=2;
}
printf("%.10lf\n",ans);
return 0;
}
D - Shik and Game
显然我们每次给一个区间的熊喂完糖,然后走到区间的第一只熊那里,依次捡金币过去即可。那么DP(只考虑捡金币的时间,正常走到出口的时间另加即可)
fi=min(fj+max(T,2(di−dj+1))f_i=min(f_j+max(T,2(d_i-d_{j+1}))fi=min(fj+max(T,2(di−dj+1))
就维护一下满足2(di−dj+1)2(d_i-d_{j+1})2(di−dj+1)的jjj中最小的fj−2dj+1f_j-2d_{j+1}fj−2dj+1即可,因为fif_ifi单调不降,所以从第一不满足2(di−dk+1)2(d_i-d_{k+1})2(di−dk+1)的kkk处做fk+Tf_k+Tfk+T这个转移即可。
#include<bits/stdc++.h>
using namespace std;
#define RI register int
int read() {
int q=0;char ch=' ';
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') q=q*10+ch-'0',ch=getchar();
return q;
}
typedef long long LL;
const int N=100005;
int n;LL f[N],d[N],mi,E,T;
int main()
{
n=read(),E=read(),T=read();
for(RI i=1;i<=n;++i) d[i]=read();
mi=1e15;
for(RI i=1,j=0;i<=n;++i) {
while(j<i&&(d[i]-d[j+1])*2>T) mi=min(mi,f[j]-2*d[j+1]),++j;
f[i]=mi+2*d[i];
if(j<i&&f[j]+T<f[i]) f[i]=f[j]+T;
}
printf("%lld\n",f[n]+E);
return 0;
}
E - Shik and Travel
首先二分答案,那么每次叶子到叶子之间的距离就不能超过我们二分出的答案。
由于每条边必须且只能走两次,所以必须一棵子树里的叶子都被走完,才能走别的叶子。我们发现不断合并即可。
假设要合并一个节点xxx的左子树和右子树,到左儿子和右儿子的边权分别是v0v_0v0和v1v_1v1。
每棵子树上维护一堆数对(a,b)(a,b)(a,b),表示从这个子树的根走到第一片叶子的费用是aaa,最后一片叶子走到根的距离是bbb。假设左子树有数对(a,b)(a,b)(a,b),右子树有数对(c,d)(c,d)(c,d),且b+c+v0+v1≤ansb+c+v_0+v_1 \leq ansb+c+v0+v1≤ans,则可以合出数对(a+v0,d+v1)(a+v_0,d+v_1)(a+v0,d+v1)。假如这个(a,b)(a,b)(a,b)已经被钦定了,我们只要找出满足条件的右子树中ddd最小的一对来合并即可。
于是我们将数对数量较少的那个,分别钦定为(a,b)(a,b)(a,b)和(c,d)(c,d)(c,d),做两次合并,每次合出的数对数量不会超过两倍数量较少的那个子树的数对数量,类似于启发式合并,复杂度是O(nlogn)O(n \log n)O(nlogn)的。如果到根节点还有数对,说明这个ansansans要大于等于最终答案,否则它小于最终答案。
总复杂度O(nlognlogans)O(n \log n \log ans)O(nlognlogans)
#include<bits/stdc++.h>
using namespace std;
#define RI register int
int read() {
int q=0;char ch=' ';
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') q=q*10+ch-'0',ch=getchar();
return q;
}
typedef long long LL;
const int N=132000;
int n;
int s[N][2];LL v[N][2];
typedef pair<LL,LL> PR;
vector<PR> q[N];
bool cmpx(PR a,PR b) {return a.first<b.first;}
bool cmpy(PR a,PR b) {return a.second<b.second;}
void dfs(int x,int lim) {
if(!s[x][0]) {q[x].push_back((PR){0,0});return;}
dfs(s[x][0],lim),dfs(s[x][1],lim);
int s0=s[x][0],s1=s[x][1],v0=v[x][0],v1=v[x][1];
int sz0=q[s0].size(),sz1=q[s1].size();
if(sz0>sz1) swap(s0,s1),swap(v0,v1),swap(sz0,sz1);
LL mi=1e9;
sort(q[s0].begin(),q[s0].end(),cmpy);
sort(q[s1].begin(),q[s1].end(),cmpx);
for(RI i=sz0-1,j=0;i>=0;--i) {
while(j<sz1&&q[s1][j].first+q[s0][i].second+v1+v0<=lim)
mi=min(mi,q[s1][j].second),++j;
if(j) q[x].push_back((PR){q[s0][i].first+v0,mi+v1});
}
sort(q[s0].begin(),q[s0].end(),cmpx);
sort(q[s1].begin(),q[s1].end(),cmpy);
mi=1e9;
for(RI i=sz0-1,j=0;i>=0;--i) {
while(j<sz1&&q[s1][j].second+q[s0][i].first+v1+v0<=lim)
mi=min(mi,q[s1][j].first),++j;
if(j) q[x].push_back((PR){mi+v1,q[s0][i].second+v0});
}
q[s0].clear(),q[s1].clear();
}
int main()
{
int x,y;
n=read();
for(RI i=2;i<=n;++i) {
x=read(),y=read();
if(s[x][0]) s[x][1]=i,v[x][1]=y;
else s[x][0]=i,v[x][0]=y;
}
LL l=0,r=1e9,res=1e9;
while(l<=r) {
LL mid=(l+r)>>1;q[1].clear(),dfs(1,mid);
if(q[1].empty()) l=mid+1;
else res=mid,r=mid-1;
}
printf("%lld\n",res);
return 0;
}
F - Shik and Copying String
又一道玄学大神题。
首先我们会发现,此题就是从某个S字母出发,每次向下方或者右下方或者右方走,直到走到T的与该字母相同的位置,所有的路径不能相交,问中间至少要填充几行。
我们可以维护T中每一个字母必须要由S中哪个字母抵达,连续的相同字母则由S中同一个字母抵达即可。
首先判断ans=0的情况。然后我们先留一行,用于左右走动。
连续一段相同的字母就用一个相同的起点即可。
会产生若干起点区间,折线紧紧地相贴。假如这个区间有kkk条折线,对于最后面的一个起点ppp,每个拐点的往前数k−1k-1k−1个格子都会被折线控制。如此如此,我们玄学地用队列维护一下区间,区间里有kkk条折线,则至少要kkk行。(加上左右走动的行,是k+1k+1k+1行)
呃,要我的理解有哪里错了,欢迎把我打一顿然后告诉我哪错了QAQ
#include<bits/stdc++.h>
using namespace std;
#define RI register int
const int N=1000005;
int n,ans,he,ta,q[N];
char S[N],T[N];
int main()
{
scanf("%d",&n);
scanf("%s",S+1),scanf("%s",T+1);
if(!strcmp(S+1,T+1)) {puts("0");return 0;}
he=1;
for(RI i=n,j=n;i>=1;--i) {
if(T[i]==T[i-1]) continue;
while(j&&(S[j]!=T[i]||j>i)) --j;
if(!j) {puts("-1");return 0;}
while(he<=ta&&q[he]-(ta-he)>i) ++he;
q[++ta]=j;if(i!=j) ans=max(ans,ta-he+1);
}
printf("%d\n",ans+1);
return 0;
}