A - Simple Calculator
分情况讨论一下,要仔细一点。
#include<bits/stdc++.h>
using namespace std;
#define RI register int
int x,y;
int main()
{
scanf("%d%d",&x,&y);
if(x==y) puts("0");
else if(y>x) printf("%d\n",min(y-x,abs(y+x)+1));
else printf("%d\n",min(abs(x+y)+1,2+abs(x-y)));
return 0;
}
B - Contiguous Repainting
除了最后一次染色的那个长度为 的区间以外的所有格子,都可以任意确定颜色(方法就是先贴着整个序列两侧用区间涂色,然后慢慢地把涂色区间向中间挪,这样就可以一格一格地确定颜色)。
那么只要用前缀和处理一下,枚举这个最后涂色的区间即可。
#include<bits/stdc++.h>
using namespace std;
#define RI register int
int read() {
int q=0,w=1;char ch=' ';
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') w=-1,ch=getchar();
while(ch>='0'&&ch<='9') q=q*10+ch-'0',ch=getchar();
return q*w;
}
const int N=100005;
typedef long long LL;
int n,K;LL a[N],s1[N],s2[N],ans;
int main()
{
n=read(),K=read();
for(RI i=1;i<=n;++i) {
LL x=read();
s1[i]=s1[i-1]+max(0LL,x),s2[i]=s2[i-1]+x;
}
for(RI i=K;i<=n;++i) {
ans=max(ans,s2[i]-s2[i-K]+s1[i-K]+s1[n]-s1[i]);
ans=max(ans,s1[i-K]+s1[n]-s1[i]);
}
printf("%lld\n",ans);
return 0;
}
C - Tetromino Tiling
"o"型的方块直接放就行了,剩下的只有:
- 两个"L"或者两个"J"或两个"I"组成 的方块
- 一个"L"一个"J"一个"I"组成 的方块
分类讨论即可。
#include<bits/stdc++.h>
using namespace std;
#define RI register int
typedef long long LL;
LL ans,a,b,c,d,e,f,g;
int main()
{
scanf("%lld%lld%lld%lld%lld%lld%lld",&a,&b,&c,&d,&e,&f,&g);
ans+=b;
if(a&&d&&e&&((a&1)+(d&1)+(e&1)>=2)) --a,--d,--e,ans+=3;
ans+=2*(a/2+d/2+e/2);
printf("%lld\n",ans);
return 0;
}
D - K-th K
这个…你就按照被钦定的位置从小到大排序,假设数对 表示在 是第 个 ,那么就钦定 ,然后找到最前面的 个空位填上 。这么搞完一轮后,再按 从小到大,把剩下的 往剩下的空位里填,同时判断合不合法即可。
#include<bits/stdc++.h>
using namespace std;
#define RI register int
int a[250005],x[505],n;
struct node{int v,x;}t[505];
bool cmp(node k1,node k2) {return k1.x<k2.x;}
int main()
{
int x;scanf("%d",&n);
for(RI i=1;i<=n;++i) scanf("%d",&x),a[x]=i,t[i]=(node){i,x};
sort(t+1,t+1+n,cmp);
int j=1;
for(RI i=1;i<=n;++i) {
for(RI k=1;k<=t[i].v-1;++k) {
while(j<n*n&&a[j]) ++j;
if(a[j]||j>t[i].x) {puts("No");return 0;}
a[j]=t[i].v;
}
}
for(RI i=1;i<=n;++i) {
for(RI k=t[i].v+1;k<=n;++k) {
while(j<n*n&&a[j]) ++j;
if(a[j]||j<t[i].x) {puts("No");return 0;}
a[j]=t[i].v;
}
}
puts("Yes");
for(RI i=1;i<=n*n;++i) printf("%d ",a[i]);
return 0;
}
E - Next or Nextnext
水完了前面四道水题,就获得了一道神题。
litble不生产题解,litble只是官方题解的翻译工,以下图都是从官方题解搬过来的。
我们考虑最终的那个排列 ,建一个图,将点 向点 连边。因为这是个排列,所以每个点出度入度都为 ,所以一定由若干环构成。
考虑其中的一个环,我们擦掉它的所有边,然后将 向 连边,可以知道 是它前面一个节点或者前面的前面一个节点。
有四种情况。
- 所有 的 都是它的前面一个节点,则环保持不变。
- 所有 的 都是它前面的前面的节点,且环为奇环,则环变成同构的另一个环
- 所有 的 都是它前面的前面的点,且原环为偶环,则这个环会被拆成两个相同大小的环。
- 有的是前面一个节点,有的又是前面的前面,则变成了一棵由一个环和若干指向环的链构成的基环内向树。
行吧,现在我们手头上只有由 构成的那张图,没有由 构成的那张图,所以我们就要反过来考虑了。
首先我们找到所有的环,先记录每个大小的环有多少个,那么每种大小可以单独考虑,DP一下,决策就是这种大小的第 个环是和前面的环合并呢,还是单独组成 图中的环。最后用乘法原理乘起来这些东西。
对于一棵基环内向树,我们考虑相邻两个“脚”(即挂在环上的链),将“脚”往环里面塞,并且要求还是一条边与它指着的节点中间最多只能插一个节点。大致如下图:
这个脚可以塞到树里的位置,就是到下一个脚之间的边,假设这些边有 条,这个脚的长度为 ,那么:
若 ,有0种方案。
若 ,有1种方案。
若 ,有2种方案。
也可以用乘法原理搞,就做完了。
#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 mod=1e9+7,N=100005;
int n,ans;
int a[N],du[N],cir[N],vis[N],footL[N],sum[N],f[N];
int qm(int x) {return x>=mod?x-mod:x;}
void workcir(int x) {
int now=0,fr=0,ed=0,frL=0;
//fr:第一个有脚的位置,ed:上一个找到的有脚的位置
//frL:第一个脚的长度,now:当前节点是从x开始走环走到的第几个点
while(cir[x]) {
++now,cir[x]=0;
if(footL[x]) {
if(!fr) ed=fr=now,frL=footL[x];
else {//塞脚
int kl=(footL[x]<now-ed)+(footL[x]<=now-ed);
ans=1LL*ans*kl%mod,ed=now;
}
}
x=a[x];
}
if(!fr) ++sum[now];//是简单环
else {//考虑第一个脚
int kl=(frL<now-ed+fr)+(frL<=now-ed+fr);
ans=1LL*ans*kl%mod;
}
}
void work() {
for(RI i=1;i<=n;++i) {
if(du[i]) continue;
int x=i,len=0;while(!cir[x]) x=a[x],++len;
footL[x]=len;//算挂在每个点上的脚长
}
ans=1;
for(RI i=1;i<=n;++i) if(cir[i]) workcir(i);
for(RI i=1;i<=n;++i) {//对每一种长度的简单环做DP
if(!sum[i]) continue;
f[0]=1;
for(RI j=1;j<=sum[i];++j) {
if(i>1&&(i&1)) f[j]=qm(f[j-1]+f[j-1]);//情况1,2
else f[j]=f[j-1];//情况1
if(j>1) f[j]=qm(f[j]+1LL*f[j-2]*(j-1)%mod*i%mod);//情况3
}
ans=1LL*ans*f[sum[i]]%mod;
}
}
int main()
{
n=read();
for(RI i=1;i<=n;++i) a[i]=read(),++du[a[i]];
for(RI i=1;i<=n;++i) {
if(vis[i]) continue;
int x=i;while(!vis[x]) vis[x]=i,x=a[x];
if(vis[x]!=i) continue;//说明i在一个脚上
while(!cir[x]) cir[x]=1,x=a[x];//给环打上是环标记
}
for(RI i=1;i<=n;++i)//判无解
if((cir[i]&&du[i]>2)||(!cir[i]&&du[i]>1)) {puts("0");return 0;}
work();
printf("%d\n",ans);
return 0;
}
F - Black Radius
先假设树上所有点都是关键点。然后设 表示距离 小于等于 的节点的集合。
我们想一个精妙的不重不漏计数方法,假设有若干 同构,我们希望只在 最小的那个位置计算贡献,则我们计算满足以下条件的数对个数:
- 不覆盖整棵树(计数完成后让答案+1即可统计整棵树被染色的情况)
- 对于与 相邻的点 ,都不存在 对于与 相邻的点 ,都不存在
所以我们发现,每个点上可以取的 存在一个上界。
设离 最远的点离 的距离为 ,显然条件1等价于 。
而条件2,考虑若存在一个这样的 ,把 看做树根。由于 能够染周围 的点,所以在 子树里的点,若存在于 中必然存在在 中,反之不存在于。而若在 子树以外的地方有一个点 和 的距离大于 ,则 中没有它,而 中有它。
因此若设 表示删掉 子树后剩余节点中找一个点,使得 离它的距离最远,则条件2满足等价于 。显然在 子树里存在到 距离最远的点时, 最小,最能产生约束。
现在看有的点不是关键点的情况。
那么对于不是关键点的点 ,若要 是与它相同的集合表示中, 最小的那个,且也是一个关键点 的某种染色集合,则这个集合必须包含以 为根,它的儿子们的子树中,包含 的那个子树中的所有节点。(有点拗口啊QAQ)
证明:
如果不包含的话,如下图,
以下的部分没包含,则
,从
走到
的距离是
,走到
再走到
的其他儿子,还能走的距离是
。而对于状态
,从
走到
后再能走的距离也是
,而且也能走到
,所以
,
不是与它相同的集合表示中
最小的那个。
于是我们就做换根DP即可。
#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=200005,inf=0x3f3f3f3f;
int h[N],ne[N<<1],to[N<<1],mx[N],se[N],d[N],sz[N];
int n,tot;LL ans;char S[N];
void add(int x,int y) {to[++tot]=y,ne[tot]=h[x],h[x]=tot;}
void dfs1(int x,int las) {
if(S[x]=='1') d[x]=0,sz[x]=1;
else d[x]=inf;
for(RI i=h[x];i;i=ne[i]) {
if(to[i]==las) continue;
int y=to[i];dfs1(y,x),sz[x]+=sz[y];
if(mx[y]+1>mx[x]) se[x]=mx[x],mx[x]=mx[y]+1;
else if(mx[y]+1>se[x]) se[x]=mx[y]+1;
if(sz[y]) d[x]=min(d[x],mx[y]+1);
}
}
void dfs2(int x,int las) {
int R=min(se[x]+1,mx[x]-1);
if(d[x]<=R) ans+=(LL)(R-d[x]+1);
for(RI i=h[x];i;i=ne[i]) {
if(to[i]==las) continue;
int y=to[i],kl=(mx[y]+1==mx[x]?se[x]+1:mx[x]+1);
if(kl>mx[y]) se[y]=mx[y],mx[y]=kl;
else if(kl>se[y]) se[y]=kl;
if(sz[1]-sz[y]) d[y]=min(d[y],kl);
dfs2(y,x);
}
}
int main()
{
int x,y;
n=read();
for(RI i=1;i<n;++i) x=read(),y=read(),add(x,y),add(y,x);
scanf("%s",S+1);
dfs1(1,0),dfs2(1,0);
printf("%lld\n",ans+1);
return 0;
}