记又一次unhappy考试(10.6)

爆零自动机
unhappy
还好有人陪我爆零。
据说题目很简单,也据说代码很短,最后知道真相的我眼泪掉下来。。。。

题解时间

T1题解
T1自爆了,还没写。

好了把T1膜完了,现在补上去。
T1的思路真是“简单清新”。图是一个带环的树,而且保证只有一个环。我们只要对于一条链上的所有小的子树依次DP,然后判断起点和终点的位置,如果起点和终点在一棵树上,那么只要子树上瞎搞就好了;如果不在一棵树上,就得考虑多棵树的影响。在统计距离的时候,采用相对距离,如P1和P2相距L1,P1和P3相距L2,可知P2和P3相距L2-L1,于是计算的时候只需要简单粗暴地给P2到P3的距离暴力减掉L2-L1,这样前面记录的距离就都不需要进行改动了,真神奇。

代码如下:

/***********************
orz orz orz nbdhhzh ypa
***********************/
#pragma GCC optimize ("O2")
#include <bits/stdc++.h>
#define C(a) memset(a,0,sizeof(a))
#define M 400010
using namespace std;
int n,m,i,j,l,lj,T,cas,cnt,S,ans,tot;
int v[M],a[M],b[M],c[M],fa[M],d[M],mx[M],f[M],p[M],q[M],he[M],to[M],ne[M];
inline void add(int x,int y){to[++tot]=y;ne[tot]=he[x];he[x]=tot;}
inline void up(int x,int y,int k){
    if(x>y) swap(x,y);
    if(k>ans||k==ans&&x<S||k==ans&&x==S&&y<T) S=x,T=y,ans=k;
}
inline void dfs(int x){
    v[x]=1;int i,y;
    for(i=he[x];y=to[i],i;i=ne[i]){
        if(!f[i]&&!cnt) if(!v[y])
            f[i]=f[i^1]=1,fa[y]=i,dfs(y);
        else{
            for(int j=x;j!=y;j=to[fa[j]^1])
                f[fa[j]]=f[fa[j]^1]=2,c[++cnt]=j;
            c[++cnt]=y;f[i]=f[i^1]=2;return;
        }
    }
}
inline void dfs1(int x){
    int i,y,mx1=x,mx2=x;
    for(d[x]=d[fa[x]]+1,mx[x]=x,i=he[x];y=to[i],i;i=ne[i])
        if(!f[i]){
            f[i]=f[i^1]=1;fa[y]=x;dfs1(y);
            if(d[mx[y]]>d[mx[x]]||d[mx[y]]==d[mx[x]]&&mx[y]<mx[x]) mx[x]=mx[y];
            if(d[mx[y]]>d[mx2]||d[mx[y]]==d[mx2]&&mx[y]<mx2){
                mx2=mx[y]; if(d[mx2]>d[mx1]||d[mx2]==d[mx1]&&mx2<mx1) swap(mx1,mx2);
            }
        }up(mx1,mx2,d[mx1]+d[mx2]+cnt-2*d[x]);
}
int main(){
    for(scanf("%d",&lj);tot=1,S=T=ans=cnt=0,lj--;){
        C(he),C(ne),C(to),C(v),C(d),C(mx),C(fa),C(f),C(q),C(p);
        for(C(a),C(b),C(c),scanf("%d",&n),i=1;i<=n;i++)
            scanf("%d%d",&a[i],&b[i]),add(a[i],b[i]),add(b[i],a[i]);
        for(dfs(1),i=2;i<=tot;i++) if(f[i]!=2) f[i]=0;
        for(i=1;i<=n;i++) v[i]=0,fa[i]=0;
        for(i=1;i<=cnt;i++) dfs1(c[i]);
        for(i=1;i<=cnt;i++) c[i+cnt]=c[i];
        for(i=1;i<=cnt*2;i++) p[i]=d[mx[c[i]]]+i;
        for(int w=0,t=i=1;i<=cnt*2;q[++w]=i,i++){
            if(q[t]==i-cnt) t++;
            if(t<=w) up(mx[c[i]],mx[c[q[t]]],p[q[t]]+d[mx[c[i]]]-i+cnt);
            while(t<=w&&((p[i]>p[q[w]])||(p[i]==p[q[w]])&&(c[i]<c[q[w]]))) w--;
        }printf("Case #%d: %d %d %d\n",++cas,n*2-ans,S,T);
    }return 0;
}

T2题解
简直就是思考题,思想难度++,实现难度–。惊讶地发现,只要选择了大于等于6个点,必定有符合条件的三个点(显然法)。所以只要求出所有可以选的情况,然后再暴力枚举最多5个点不可能的情况减掉,然后就过了。。。。过了。。。。
灰常暴力。。。

代码如下:

/*******************
orz orz nbdhhzh ypa
*******************/
#pragma GCC optimize ("O2")
#include <bits/stdc++.h>
#define P 1000000007
using namespace std;
int n,m,T,cas,ans,a,b,c,d,e;
int f[60][60];
bool ok(int x,int y,int z){return f[x][y]==f[y][z]&&f[x][y]==f[x][z];}
int main(){
    for(scanf("%d",&T),cas=1;cas<=T;cas++){
        memset(f,0,sizeof(f));scanf("%d%d",&n,&m);
        ans=((1LL<<n)-1-n-n*(n-1)/2)%P;
        for(e=1;e<=m;e++) scanf("%d%d",&a,&b),f[a][b]=f[b][a]=1;
        for(a=1;a<=n;a++) for(b=a+1;b<=n;b++) for(c=b+1;c<=n;c++){
            for(ans-=!ok(a,b,c),d=c+1;d<=n;d++)
                for(ans-=!(ok(a,b,c)|ok(a,b,d)|ok(a,c,d)|ok(b,c,d)),e=d+1;e<=n;e++)
                    ans-=!(ok(a,b,c)|ok(a,b,d)|ok(a,b,e)|ok(a,c,d)|ok(a,c,e)|
                        ok(a,d,e)|ok(b,c,d)|ok(b,c,e)|ok(b,d,e)|ok(c,d,e));
            ans+=(ans<0)?P:0;
        }printf("Case #%d: %d\n",cas,ans);
    }return 0;
}

T3题解
T3有个灰常神奇的思路。非常显然的是,如果你把一个增加
的字母或是替换的字母或是交换过来的字母给删了,建议你去看一下医生。然后黈黈黈提出了一个根本不理解为什么的方案,举个栗子:
agccct
tcca
一串如果要变到二串,只要把一串
’a‘和’t‘之间的东西全删了,然后交换’a‘和’t‘然后再把’c‘、’c‘给加进去就好了,于是就神奇了。。。。
对于一串的某个字母,只要在二串里相同位置向前寻找第一个相同的字母,然后使用以上的操作即可。

代码如下:

/******************
orz orz nbdhhzh ypa
******************/
#pragma GCC optimize ("O2")
#include <bits/stdc++.h>
using namespace std;
int t1,t2,t3,t4,n,m,i,j,l1,l2,t,tt1,tt2;
int f[4010][4010],g[2][4010][30];
char s1[4010],s2[4010];
int main(){
    scanf("%d%d%d%d%s%s",&t1,&t2,&t3,&t4,s1+1,s2+1);
    for(l1=strlen(s1+1),l2=strlen(s2+1),i=1;i<=l1;i++){
        for(f[i][0]=i*t2,j=0;j<26;j++) g[0][i][j]=g[0][i-1][j];
        if(i>1) g[0][i][s1[i-1]-97]=i-1;
    }for(i=1;i<=l2;i++){
        for(f[0][i]=i*t1,j=0;j<26;j++) g[1][i][j]=g[1][i-1][j];
        if(i>1) g[1][i][s2[i-1]-97]=i-1;
    }for(i=1;i<=l1;i++) for(j=1;j<=l2;j++){
        f[i][j]=min(f[i-1][j]+t2,f[i][j-1]+t1);
        t=(s1[i]!=s2[j])*t3+f[i-1][j-1];
        if(f[i][j]>t) f[i][j]=t;
        if((tt1=g[0][i][s2[j]-97])&&(tt2=g[1][j][s1[i]-97]))
            t=f[tt1-1][tt2-1]+(i-tt1-1)*t2+(j-tt2-1)*t1+t4,
            f[i][j]=(f[i][j]>t)?t:f[i][j];
    }printf("%d\n",f[l1][l2]);return 0;
}

整天氧气开开我已经没有希望了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值