AGC008
我突然想起来 Madoka 的剧场版第三集我当时听取了一些建议打算等到看完 12 集之后再去看。然后我现在就差这个了。
传说中让清华牛子自闭的神番。
今天好像有点困,小细节分讨签到题有点切不动的样子。
(第二天:)
观测了多头鲜花。来点读后感。
感觉在座的各位大概都可以写出这种鲜花,如果主观上想去写了。反正我没有这个勇气去写。
所以我之前的相当多的一些东西写出来就变了味了。所以说我平时看起来相当魔怔。
毕竟我不可能知道在座的各位到底是谁。
[AGC008A] Simple Calculator
我为啥这玩意交了这么多发.jpg
int x,y;
int main(){
scanf("%d%d",&x,&y);
if(x==y){
puts("0");return 0;
}
if(!x||!y){
if(x>y)printf("%d\n",abs(x+y)+1);
else printf("%d\n",abs(x+y));
return 0;
}
if(x>=0&&y>=0){
if(x<y)printf("%d\n",y-x);
else printf("%d\n",x-y+2);
}
else if((x>=0&&y<0)||(x<0&&y>=0)){
printf("%d\n",min(abs(abs(y)-abs(x))+1,max(x,y)-min(x,y)));
}
else{
if(x<y)printf("%d\n",y-x);
else printf("%d\n",x-y+2);
}
return 0;
}
[AGC008B] Contiguous Repainting
弱智题。前面随便选点,最后一次连续就行。枚举最后一次的区间暴力算就行了。
int n,k;
long long ans,a[100010],pre[100010],suf[100010],sum[100010];
int main(){
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)scanf("%lld",&a[i]),sum[i]=sum[i-1]+a[i];
for(int i=1;i<=n;i++)pre[i]=pre[i-1]+(a[i]>0?a[i]:0);
for(int i=n;i>=1;i--)suf[i]=suf[i+1]+(a[i]>0?a[i]:0);
for(int i=k;i<=n;i++)ans=max(ans,pre[i-k]+suf[i+1]+max(sum[i]-sum[i-k],0ll));
printf("%lld\n",ans);
return 0;
}
[AGC008C] Tetromino Tiling
我为啥这玩意也交了这么多发.jpg
long long ans,a[10];
int main(){
for(int i=1;i<=7;i++)scanf("%lld",&a[i]);
ans=a[2]+a[1]/2*2+a[4]/2*2+a[5]/2*2;
if(a[1]&&a[4]&&a[5]){
a[1]--;a[4]--;a[5]--;
ans=max(ans,a[2]+3+a[1]/2*2+a[4]/2*2+a[5]/2*2);
}
printf("%lld\n",ans);
return 0;
}
[AGC008D] K-th K
原先是每个数填在能填的最靠近它的位置,然后交上去挂了一半。后来想了想这样填的话后面的数会挡住前面的数在后面放的位置,而前面还没有填数。
然后换个思路,每次找 \(pos\) 最小的 \(i\) ,在最前面 \(i-1\) 个位置填上。最大的同理操作。
int n,pos[250010],a[250010];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
int x;scanf("%d",&x);
a[x]=pos[x]=i;
}
int p=1;
for(int i=1;i<=n*n;i++){
if(pos[i]){
int l=a[i]-1;
for(;l&&p<i;p++){
if(a[p])continue;
a[p]=a[i],l--;
}
if(l){
puts("No");return 0;
}
}
}
p=n*n;
for(int i=n*n;i>=1;i--){
if(pos[i]){
int r=n-a[i];
for(;r&&p>i;p--){
if(a[p])continue;
a[p]=a[i],r--;
}
if(r){
puts("No");return 0;
}
}
}
puts("Yes");
for(int i=1;i<=n*n;i++)printf("%d ",a[i]);
printf("\n");
return 0;
}
[AGC008E] Next or Nextnext
什么世道啊,不是 sb 分讨构造就是黑。这是想让我平均一道题吃五发罚时。除了水题就是神题那我咋刷啊。(所以我压根没开VP)
居然有标签。啥 dp?数学?寄环树?那没事了。
神题,不是很懂。感觉比 F 神奇,虽然评分稍微低一点。
首先套路是排列 \(i\rightarrow p_i\) 连边。然后排列就成了一堆环。题目的两种匹配就相当于两个变换:\(i\) 连到下一个和 \(i\) 连到下一个的下一个。
这样有四种情况:
- 都连到下一个,就是个和原来同构的环。
- 都连到下一个的下一个,且环是奇环,此时还是和原来同构。
- 都连到下一个的下一个,且环是偶环,此时会分成两个大小为原来一半的环。
- 两种都有,会成基环树。(麻了我现在看见基环树就想打寄环树)
现在我们知道一个排列生成了一堆环或者基环树。分开考虑。
如果是基环树,那么我们如果要把它还原成一个环,就需要把伸出来的链缩到环里。对于相邻的两个链,前面的一个链就只能塞到这条链和下一条链之间。而且既然出现了一条链,那么链上的点一定是两两隔一个环上的点。那么根据两条链之间的长度和链长,有三种情况:塞不下,只有一种塞法,有两种塞法(链上的第一个点是否和环上的点挨着)。
如果是环,考虑逐个把环拼起来。对于每个环长 \(len\) ,个数有 \(cnt_{len}\) 个,枚举有 \(2i\) 个和其他的环拼起来。那么方案数是一堆组合数:
- 选出来的方案数,\(\dbinom{cnt_{len}}{2j}\)。
- 把环拼起来的方案数,\(\dfrac{\binom{2i}{i}i!}{2^i}\)。
- 每对环的拼法, \(len^i\) 。
- \(i\) 是奇数的时候剩下的环都有两种同构的方式, \(2^{cnt_{len}-2i}\) 。注意 \(i=1\) 的时候不能算数。
那就完了。代码好长。
const int mod=1000000007;
int n,ans=1,a[100010],ind[100010],len[100010],jc[100010],inv[100010],cnt[100010];
bool v[100010],belong[100010],in[100010];
int stk[100010],top;
void dfs1(int x){
if(v[x]){
if(in[x]){
for(int i=top;i>=1;i--){
belong[stk[i]]=true;
if(stk[i]==x)break;
}
}
return;
}
v[x]=in[x]=true;stk[++top]=x;
dfs1(a[x]);
in[x]=false;
}
bool dfs2(int x){
if(v[x])return true;
v[x]=true;
stk[++top]=len[x];
if(dfs2(a[x])&&!len[x])return true;
return false;
}
queue<int>q;
int qpow(int a,int b){
int ans=1;
while(b){
if(b&1)ans=1ll*ans*a%mod;
a=1ll*a*a%mod;
b>>=1;
}
return ans;
}
int C(int n,int m){
if(n<m)return 0;
return 1ll*jc[n]*inv[m]%mod*inv[n-m]%mod;
}
int main(){
scanf("%d",&n);jc[0]=inv[0]=1;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);ind[a[i]]++;
jc[i]=1ll*jc[i-1]*i%mod;
}
inv[n]=qpow(jc[n],mod-2);
for(int i=n-1;i>=1;i--)inv[i]=1ll*inv[i+1]*(i+1)%mod;
for(int i=1;i<=n;i++)dfs1(i);
for(int i=1;i<=n;i++){
v[i]=false;
if((belong[i]&&ind[i]>=3)||(!belong[i]&&ind[i]>=2)){
puts("0");return 0;
}
}
for(int i=1;i<=n;i++)if(!ind[i])q.push(i);
while(!q.empty()){
int x=q.front();q.pop();
len[a[x]]=len[x]+1;
if(!belong[a[x]])q.push(a[x]);
}
for(int i=1;i<=n;i++){
if(!v[i]&&belong[i]){
top=0;
if(dfs2(i))cnt[top]++;
else{
int pos=0;
for(int j=1;j<=top;j++){
if(stk[j]){
if(pos){
int ret=j-stk[j];
if(ret<pos){
puts("0");return 0;
}
if(ret>pos&&top>=2)ans=2ll*ans%mod;
}
pos=j;
}
}
for(int j=1;j<=top;j++){
if(stk[j]){
int ret=j-stk[j]+top;
if(ret<pos){
puts("0");return 0;
}
if(ret>pos&&top>=2)ans=2ll*ans%mod;
break;
}
}
}
}
}
for(int i=1;i<=n;i++){
if(cnt[i]){
int ret=0;
for(int j=0;j<=(cnt[i]>>1);j++){
int tmp=1ll*C(cnt[i],2*j)*C(2*j,j)%mod*jc[j]%mod*qpow((mod+1)>>1,j)%mod*qpow(i,j)%mod;
if((i&1)&&i!=1)tmp=1ll*tmp*qpow(2,cnt[i]-2*j)%mod;
ret=(ret+tmp)%mod;
}
ans=1ll*ans*ret%mod;
}
}
printf("%d\n",ans);
return 0;
}
[AGC008F] Black Radius
看起来好可做啊。
首先遇到这种不知道是什么东西的东西那显然是 dp。
先看部分分全是 \(1\)。
考虑把每个局面用一个比较压缩的形式表示,即设 \((x,d)\) 为在 \(x\) 处进行一次 \(d\) 距离的染色(为了方便先把整棵树都被染色的情况扔掉)。那么如果不重复计数,就只要考虑把每个相同局面中 \(d\) 最小的计入答案。
那么每个节点 \(x\) 都有一个约束条件:不能有一个相邻节点 \(y\) 满足 \((x,d)=(y,d-1)\)。显然每个节点都有一个 \(d\) 的上界。二分这个 \(d\) ,复杂度 \(O(n^2\log n)\)
不如看看这个约束条件到底影响多少点。设根为 \(x\),那么 \((x,d)\) 和 \((y,d-1)\) 在 \(y\) 子树内的染色范围是一样的,而在其他子树,\((y,d-1)\) 就相当于 \((x,d-2)\) 。所以考虑 \(x\) 的所有子树,\(x\) 的 \(d\) 就是第二深的子树深度 \(+1\) 。换根即可。对了这个 \(d\) 显然小于最深的子树深度。(怎么说呢手模结果可能有点对不上,因为有个 \(2\) 的下界)
到这里我们解决了全是关键点的情况。考虑有不是关键点的问题。发现好像要整不少深度什么的东西超级难搞,于是看题解。好吊。
如果 \(x\) 不是关键点,那么假如说 \((x,d)\) 和某个关键点 \((y,d_1)\) 是等价的,观察得到只需要完全覆盖 \(y\) 所在的子树就好。这确定了下界。于是还是换根。
int n;
char s[200010];
struct node{
int v,next;
}edge[400010];
int t,head[200010];
void add(int u,int v){
edge[++t].v=v;edge[t].next=head[u];head[u]=t;
}
int siz[200010],dis[200010],mx[200010],se[200010];
long long ans;
void dfs1(int x,int f){
if(s[x]=='1')siz[x]=1;
else dis[x]=0x3f3f3f3f;
for(int i=head[x];i;i=edge[i].next){
if(edge[i].v!=f){
dfs1(edge[i].v,x);
siz[x]+=siz[edge[i].v];
if(mx[edge[i].v]+1>mx[x])se[x]=mx[x],mx[x]=mx[edge[i].v]+1;
else if(mx[edge[i].v]+1>se[x])se[x]=mx[edge[i].v]+1;
if(siz[edge[i].v])dis[x]=min(dis[x],mx[edge[i].v]+1);
}
}
}
void dfs2(int x,int f){
ans+=max(0,min(mx[x],se[x]+2)-dis[x]);
for(int i=head[x];i;i=edge[i].next){
if(edge[i].v!=f){
int ret;
if(mx[x]==mx[edge[i].v]+1)ret=se[x]+1;
else ret=mx[x]+1;
if(ret>mx[edge[i].v])se[edge[i].v]=mx[edge[i].v],mx[edge[i].v]=ret;
else if(ret>se[edge[i].v])se[edge[i].v]=ret;
if(siz[1]-siz[edge[i].v])dis[edge[i].v]=min(dis[edge[i].v],ret);
dfs2(edge[i].v,x);
}
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<n;i++){
int u,v;scanf("%d%d",&u,&v);
add(u,v);add(v,u);
}
scanf("%s",s+1);
dfs1(1,0);dfs2(1,0);
printf("%lld\n",ans+1);
return 0;
}
完了。