近期模拟赛

大杂烩 订正了

或许这两天我有点水逆 牛客的比赛 大家觉得很简单的东西 我都不会写 我是不是最近要暴毙了??

这两天的比赛 补多少 写多少 不定更新

Day1 100+27+40

T1 一个数字串S 一个S打乱后的T串 用S - T 注意是转化后的数字相减 )考场上以为是 S到T 得到一个新的数字 然后随机抽一个数字 得到一个字符串P

现在给定P 那么求一组合法解以及删掉的数字 

其实算是一个构造题目吧

不过 这个很巧妙的小结论 大家还是有同学看出来的 我考场上按了好几遍计算器 我发现了这个东西 不过原来确实见过

也就是 对于 一个数字 他的每一位数字任意排列 将会得到一个新的数字 那么此时用较大的数字 减去 较小的数字 我们将会得到一个差值

那么这个差值 一定是9的倍数 那么 怎么找到这个数字 我们考虑 什么样子的数字是9的倍数

其实 我们要注重积累 我原来写计蒜客 普及 模拟赛 遇到一个题目 给你一个区间$[L,R]$ 这个区间的数字 L,L+1,L+2,....R 组在一起 构成一个数字

判断这个数字是不是9的倍数 L,R是1e12级别的

那么存在一个定理 是不是9的倍数 只需要判断 所有位的数字之和 是不是9的倍数即可 那么就是一个等差数列求和

为什么 正确呢 大家利用同余证明一下即可 其实 我很早发现9就是一个很神奇的数字 他的所有倍数 的 个位数 能把1-9都表示一边 看似没有用 

那么 考虑这个数字是谁 只需要所有位数字加起来 然后 % 9 再用 9 这个数字减去即可

那么 有的同学就开始暴搜全排列 qwq 不过 我们发现此时这个东西一个能表示成9x的形式 那么 10x-x=9x 题目允许前导零 所以我们直接求X即可

注意除的时候处理一下即可 放考场代码 qwq 不过乱搞有50 或者 80??

//打乱之后相减一定是9的倍数 
//构造10x-x=9x=num
//考虑删去的数字是什么 末尾添上一个数字 能 整除9 即可
#include<bits/stdc++.h>
using namespace std;
const int N=500100;
char p[N];
int main() {
    freopen("yukikaze.in","r",stdin);
    freopen("yukikaze.out","w",stdout);
    scanf("%s",p+1);
    int lenp=strlen(p+1);
    int ans=0;
    for(int i=1;i<=lenp;i++)
        ans+=(int)(p[i]-'0');
    int res=9-(ans%9);
    printf("%d\n",res);
    p[++lenp]=(char)(res+'0');
    res=0;
    for(int i=1;i<=lenp;i++) {
        res=res*10+(int)(p[i]-'0');
        p[i]=(char)((res/9)+'0');
        res%=9;
    }//求差
    for(int i=1;i<=lenp;i++) 
        printf("%d",(int)(p[i]-'0'));
    puts("0");//10倍即可 
    printf("0");
    for(int i=1;i<=lenp;i++)
        printf("%d",(int)(p[i]-'0'));
    return 0;
}
View Code

T2 对不起 我全场最低 27分

为什么呢 我小数据点的都T了 我看了好久 发现我没有(1)LCA 要哭了  其实预处理 也可以qwq

其实对于链的情况 确实很好推 

这里随便口胡一下 对于阶乘 以及 2^n 这些计数的理解 

首先 阶乘吧 就是带一种 全排列的思想在里面 没有任何限制的这种 我们一般知道有多少个位置 此时 没有限制的打乱 此时方案数就是阶乘

然后考虑 2^n 其实就是对应每个位置 都有两种选择 一共有n个位置 其实利用 乘法原理 不难发现

然后乘法原理 和 加法原理 大家 都会 就不管了

那么 对于一个长度是偶数的链 那么一定是前半部分 和 后半部分 匹配 此时方案数是 前半部分 * 后半部分 

那么 前半部分 是 (n/2)! 后半部分亦然 所以方案数是 (n/2)!*(n/2)!

那么链是偶数 此时 也是前半部分 和 后半部分 此时 考虑 中间那个数字 肯定是放哪无所谓 

所以 前半部分*后半部分*中间数字的方案数 答案就是 n *((n-1)/ 2))!*((n-1)/ 2)!

对于正解 我还没写 明天考虑 抽时间写一下

T3 暴搜+打表 

说实话 我信仰 14!能跑出来hh 结果等到他十几分钟hh

不过 有40分 

//暴搜+打表 考RP拿分 
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &x) {
    x=0; T f=1,ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();}
    while(isdigit(ch))  {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    x*=f;
}
const int mod=1000000007;
ll n,ans=0,Sta[25],vis[25],tot[25],chosen[25],order[25];
inline int gcd(int a,int b){
    return b?gcd(b,a%b):a;
}
inline void dfs(int x) {
    if(x==n+1) {
        int res=0,s=0;
        memset(Sta,0,sizeof(Sta));
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=n;i++) {
            if(i==1) s=order[i],Sta[i]=s;
            else s=gcd(s,order[i]),Sta[i]=s;
//            cout<<s<<endl; 
        }
//        puts("");
        for(int i=1;i<=n;i++) {
            if(!vis[Sta[i]]) res++,vis[Sta[i]]=1;
        }
        tot[res]=(tot[res]+1)%mod;
//        cout<<tot[res]<<endl;
        if(ans<res) {
            ans=res;
        }
        return;
    }
    for(int i=1;i<=n;i++) {
        if(chosen[i]) continue;
        order[x]=i;
        chosen[i]=i;
        dfs(x+1);
        chosen[i]=0;
        order[x]=0;
    }
}
int main() {
//    freopen("1.in","r",stdin);
    freopen("shimakaze.in","r",stdin);
    freopen("shimakaze.out","w",stdout);
    read(n);
    if(n<=15) {
        if(n==2) puts("1");
        else if(n==6) puts("120");
        else if(n==7) puts("600");
        else if(n==8) puts("240");
        else if(n==9) puts("1440");
        else if(n==10) puts("15120");
        else if(n==11) puts("120960");
        else if(n==12) puts("7015680");
        else if(n==13) puts("69672960");
        else {
            dfs(1);
            printf("%d\n",tot[ans]);
        }
        return 0;
    } 
    else {
        if(n==25) puts("54818958");
        if(n==94657) puts("218873752");
        if(n==674591) puts("432028765");
        if(n==741964) puts("17832372");
    }
    return 0;
}
View Code

T1

大家都切切切了 

是个链表 模拟一下 每次更新前驱 以及 后继即可 

#include <bits/stdc++.h>
using namespace std;
const int N=1e6+100;
char s[N];
int nxt[N],pre[N],res,vis[N];
int main() {
    freopen("amagiri.in","r",stdin);
    freopen("amagiri.out","w",stdout);
    scanf("%s",s+1);
    int len=strlen(s+1);
    for(int i=2;i<len;i++) {
        nxt[i]=i+1;
        pre[i]=i-1;
    }
    pre[len]=len-1;
    nxt[1]=2;
    for(int i=1;i<=len;i++) {
        if(vis[i]) continue;
        if(nxt[i]>0&&s[i]==s[nxt[i]]) {
            res++;
            vis[i]=1;
            vis[nxt[i]]=1;
            nxt[pre[i]]=nxt[nxt[i]];
            pre[nxt[nxt[i]]]=pre[i];
        }
        else if(pre[i]>0&&s[i]==s[pre[i]]) {
            res++;
            vis[pre[i]]=1;
            vis[i]=1;
            nxt[pre[pre[i]]]=nxt[i];
            pre[nxt[i]]=pre[pre[i]];
        }
    }
    if(res&1) puts("amagiri");
    else puts("PT-109");
    return 0;
}
View Code

T2

出题人 为了不让 我 爆零 也是费心了 编译正确+17分 不过有神仙AKqwq

T3

预处理 阶乘的数组就开到20??? 考场上光顾着玩小恐龙了

暴搜代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
template<typename T>inline void read(T &x) {
    x=0;T f=1,ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    x*=f;
}
const int mod=1000000007;
typedef long long ll;
ll fac[1<<17];
int ress=1,res=1,n,num,m,ans;
int chosen[20],orderr[20],order[20],b[20],mark[20];
inline void dfs(int x) {
    if(x==ress+1) {
        int res=ress;
        for(int i=1;i<=ress;i++) orderr[i]=order[i]; 
        while(res>1) {
            num=0;
            for(int i=1;2*i<=res;i++) {
                if(orderr[2*i]==1&&mark[orderr[2*i-1]]) b[++num]=orderr[2*i-1];
                else if(orderr[2*i-1]==1&&mark[orderr[2*i]]) b[++num]=orderr[2*i];
                else if(orderr[2*i]>orderr[2*i-1]||((orderr[2*i-1]==1)&&!mark[orderr[2*i]])) {
                    b[++num]=orderr[2*i-1];
                }
                else if(orderr[2*i]<orderr[2*i-1]||((orderr[2*i]==1)&&!mark[orderr[2*i-1]])) {
                    b[++num]=orderr[2*i];
                }
            } 
//            for(int i=1;i<=num;i++) cout<<b[i]<<' ';
//            puts("");
            if(num==1) {
                if(b[num]==1) ans=(ans+1)%mod;
                else break;
            }
            res=num;
            for(int i=1;i<=num;i++) orderr[i]=b[i];
        }
//        res=ress;
        return ;
    }
    for(int i=1;i<=ress;i++) {
        if(chosen[i]) continue;
        order[x]=i;
        chosen[i]=i;
        dfs(x+1);
        chosen[i]=0;
        order[x]=0;
    }
}
inline ll ksm(int a,int b) {
    ll res=1;
    while(b) {
        if(b&1) res=(res*a)%mod;
        a=a*a%mod;
        b>>=1;
    }
    return res;
}
signed main() {
    freopen("shiratsuyu.in","r",stdin);
    freopen("shiratsuyu.out","w",stdout);
    fac[0]=1;
    for(int i=1;i<=20;i++) {
        fac[i]=(ll)(fac[i-1]*i)%mod;
    }
    read(n); read(m);
    int x;
    for(int i=1;i<=m;i++) {
        read(x);
        mark[x]=1;
    }
    for(int i=1;i<=n;i++) ress*=2;
    res=ress;
//    cout<<res<<endl;
    //for(int i=1;i<=n;i++) pos[i]=i;
    if(!m) {
        printf("%lld\n",fac[ress]%mod);
        return 0;
    }
    if(n==1) {
        if(m==1) puts("0");
        if(!m) puts("2");
        return 0;
    }
    else if(n<=3) {
//        cout<<"((";
        dfs(1);
        cout<<ans<<endl;
    }
}
View Code

T1 按开关状压 或者 暴搜

我这里有一个贼 复杂的状压 跟题解的状态不太一样qwq 等下补好了 

牛客写不来 我比较喜欢看计蒜客hh

也不是每一道都会写啊 所以 从很多比赛中 挑出来几道 简单整理一下

提高组模拟赛#4 T1 

众所周知,位运算有与,或,异或三种。

  • 与:相同位的两个数字都为 1,则为 1;若有一个不为 1,则为 0。
  • 或:相同位只要一个为 1 即为 1
  • 异或:相同位不同则为 1,相同则为 0 。

小 ZZ 觉得她们非常的有趣,为了体现自己的强大,小 ZZ 一口气学会了三种运算,并出了一道题准备考考你。

给出 l,r 以及运算 ⨁ ,询问 [l,r][l,r] 的每一个数通过 ⨁ 运算后的值。

其中运算会给出,op = 1 运算为与,op=2运算为或,op=3 运算为异或。

看了一眼数据范围 L,R是1e16的嘿嘿 for循环会T了吧

刚一看 感觉特别不可做的样子 那么不妨 换个思路思考  1e16 表示成二进制数字 不会超过60位

那么 我们就来考虑 二进制的每一位的贡献 

and操作 

每个数这个二进制位是 1 贡献为1  否则为0 。 如果 L 这一位为 0则贡献为0 ,否则就看是否有进位,如果没有进位则贡献为 0,反之为1;

or操作

每个数这个二进制位是0 贡献为0 否则是1 。 如果 L 这一位为0则贡献为0,否则就看是否有进位,如果没有进位则贡献为 0,反之为 1

xor操作

考虑每一二进制位的进位情况,如:

000 1111000011110000 11 不难发现中间的异或均为 0,关键就是看两边,如果1的个数为奇数,则贡献为1 。

注意特殊考虑第一位的情况:01010101....

#include<bits/stdc++.h>
using namespace std;
template<typename T>inline void read(T &x) {
    x=0;T f=1,ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    x*=f;
}
typedef long long ll;
ll n,l,r,op;
inline ll slove_and() {
    ll len=r-l+1;
    ll sum=0,lim=1;
    ll ans=0;
    for(int i=1;i<=61;i++) {
        if(sum+len-1<lim) ans+=(l&(1ll<<i-1));
        sum+=(l&(1ll<<i-1));
        lim*=2;
    }
    return ans;
}
inline ll slove_or() {
    ll len=r-l+1;
    ll sum=0,lim=1;
    ll ans=0;
    for(int i=1;i<=61;i++) {
        if(l&(1ll<<i-1)) ans+=1ll<<i-1;
        else if(sum+len-1>=lim) ans+=(1ll<<i-1);
        cout<<ans<<endl;
        sum+=(l&(1ll<<i-1));
        lim*=2;
    }
    return ans;
}
inline ll slove_xor() {
    ll len=r-l+1;
    ll sum=0,lim=1;
    ll ans=0;
    for(int i=1;i<=61;i++) {
        if(i==1) {
            if(len&1) {
                if(l&1) ans+=(len+1>>1)&1;
                else ans+=(len>>1)&1;
            } 
            else ans+=(len>>1)&1;
        }
        else if(sum+len-1<lim) {
            if((l&(1ll<<i-1))&&(len&1)) ans+=(1ll<<i-1);    
        }
        else {
            ll a=lim-sum;
            ll b=(len-(lim-sum))%lim;
            if(((l&(1ll<<i-1))&&(a&1))^((r&(1ll<<i-1))&&(b&1))) ans+=(1ll<<i-1);
        }
        sum+=(l&(1ll<<i-1));
        lim*=2;
    }
    return ans;
}
int main() {
    read(n);
    for(int i=1;i<=n;i++) {
        read(l); read(r); read(op);
        if(op==1) printf("%lld\n",slove_and());
        else if(op==2) printf("%lld\n",slove_or());
        else printf("%lld\n",slove_xor());
    }
    return 0;
}

T2 是个不错的树形背包 我今天会写一下 

给定一棵树 每个点有点权 定义 一个链的长度 是这个链上节点的数量 求可以删除一些节点 代价是点权 让这个树 最长的链的长度小于L 求最小代价

是一个树形背包的东西 $dp[x][i]$ 表示x这个节点向上传递 i 个节点的最小代价 

那么对于每个节点 都有保留 或者删除的操作 我们考虑转移 

当我们要保留节点 $dp[x][max(i,j+1)]=min{dp[x][i]+dp[y][j]},i,j<m$

对于删除节点 $dp[x][0]=\sum dp[y][i]+a[x],i<m$

注意 i 的枚举范围是[1,sizex] 而 j 的枚举范围是[1,sizey]

 

#include<bits/stdc++.h>
using namespace std;
template<typename T>inline void read(T &x) {
    x=0;T f=1,ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    x*=f;
}
const int N=5100;
const int inf=1<<30;
struct gg {
    int y,next;
}a[N<<1];
int n,m,tot,l,x,y,w[N],lin[N],size[N],f[N][N],g[N];
inline void add(int x,int y) {
    a[++tot].y=y;
    a[tot].next=lin[x];
    lin[x]=tot;
} 
inline void dfs(int x,int fa) {
    size[x]=1;f[x][0]=w[x];f[x][1]=0;
    for(int i=lin[x];i;i=a[i].next) {
        int y=a[i].y;
        if(y==fa) continue;
        dfs(y,x);
        for(int j=0;j<=min(l-1,size[x]);j++) {
            for(int k=0;k<=min(l-1,size[y]);k++) {
                if(!j) g[0]=min(f[x][0]+f[y][k],g[0]);
                else if(j+k<l) g[max(j,k+1)]=min(g[max(j,k+1)],f[x][j]+f[y][k]);
            }
        }
        for(int j=0;j<=max(size[x],size[y]+1);j++) f[x][j]=g[j],g[j]=inf;
        size[x]+=size[y];
    }
}
int main() {
    read(n); read(l);
    memset(f,0x3f,sizeof(f));
    memset(g,0x3f,sizeof(g));
    for(int i=1;i<=n;i++) read(w[i]);    
    for(int i=1;i<n;i++) {
        read(x); read(y);
        add(x,y); add(y,x);
    }
    dfs(1,0);
    int ans=inf;
    for(int j=0;j<=l;j++) {
        ans=min(ans,f[1][j]);
    }
    printf("%d\n",ans);
    //printf("%d\n",dp[1][l]);
    return 0;
}

求一个长度为n的字符串 给你所有长度为m的子串 求这个字符串qwq

感觉特别不可做 考场上 最后心态爆炸之后 最后十分钟写了个爆搜 水了10分 其实特别遗憾吧

数据点中有一个 m=2 的测试点 我当时并没有注意到 

不过确实要提到 poj 文字游戏那道题目 poj1386

这个题目 我们只需要记录首字母 和 尾字母即可 其实对应的就是题目中m=2 的情况 这个确实是在 提醒我们是欧拉路

考场上并没有想到可以连边 不过确实是 没有想到的一点 

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &x) {
    x=0;T f=1,ch=getchar();
    while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    x*=f;
}
const ll N=1e6+10;
int n,m,p,l,r,root=1,flag,f[N];
char s[N];
string c[N],ans;
map<string,int>b;
vector<int>a[N];
inline void edge(string s,string t) {
    if(!b[s]) b[s]=++p,c[p]=s;
    if(!b[t]) b[t]=++p,c[p]=t;
    l=b[s],r=b[t];
    f[l]--,f[r]++;//有向图的起点满足入度-出度=1
    a[r].push_back(l);
}
inline void dfs(int x) {
    while(a[x].size()) {
        int y=a[x].back();
        a[x].pop_back();
        dfs(y);
    }
    if(flag) ans.push_back(c[x][m-2]);
    else flag=1,ans=c[x];
}
int main() {
    read(n); read(m);
    for(int i=1;i<=n-m+1;i++) {
        scanf("%s",s+1);
        string x,y;
        for(int j=1;j<=m;j++) {
            if(j<m) x+=s[j];
            if(j>1) y+=s[j];
        }
        edge(x,y);
    }
    for(int i=1;i<=p;i++) {
        if(f[i]==1) {root=i;break;}
    }
    dfs(root);
    cout<<ans<<endl;
}

二分图 博弈 给定一张二分图 可以再某个点上放棋子 两个人轮流移动 谁不能动谁输 问先手是否必胜

首先 我们分析一下 谁赢 不难想到 对于一个二分图 我们起点是匹配点的时候 先手是必胜的 因为 后手没有办法把棋子移到非匹配点上 否则存在增广路 当起点是非匹配点是 后手必胜 

首先 假设一个点在最大匹配上 那么可以通过最大匹配上的一条边 一道另一个匹配点上 此时在剩下的图中不必再最大匹配上  

未匹配点:没有对面的点与之匹配  可匹配点:有对面的点与之匹配,但是对面的点也有别的选择 / /都是后手必胜

 

必然匹配点:有对面的点与之匹配,且对面的点仅可以与这点匹配 

如果一个点在二分图上是必然匹配点,那么先手必胜,否则后手胜。然后跑一遍最大匹配, 再判断每个点是否是最大匹配点即可。

#include<bits/stdc++.h>
using namespace std;
template<typename T>inline void read(T &x) {
    x=0;T f=1,ch=getchar();
    while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    x*=f;
}
const int N=1e4+10; 
int n,m,tot,x,y,fa,vis[N],lin[N],match[N];
struct gg {
    int y,next;
}a[N<<1];
inline void add(int x,int y) {
    a[++tot].y=y;
    a[tot].next=lin[x];
    lin[x]=tot;
}
inline bool dfs(int x) {
    for(int i=lin[x];i;i=a[i].next) {
        int y=a[i].y;
        if(!vis[y]&&y!=fa) {
            vis[y]=1;
            if(!match[y]||dfs(match[y])) {
                match[y]=x;
                match[x]=y;
                return 1;
            }
        }
    }
    return 0;
} 
int main() {
    read(n); read(m);
    for(int i=1;i<=m;i++) {
        read(x); read(y);
        add(x,y); add(y,x);
    }
    for(int i=1;i<=n;i++) {
        if(!match[i]) {
            memset(vis,0,sizeof(vis));
            dfs(i);
        }
    } 
    for(int i=1;i<=n;i++) {
        if(!match[i]) puts("Elephant");//未匹配点 
        else {
            int x=match[i];
            fa=i;
            match[x]=match[i]=0;
            memset(vis,0,sizeof(vis));
            if(dfs(x))  puts("Elephant");//可匹配点 
            else puts("Hamster"),match[i]=x,match[x]=i;//必然匹配点 
        }
    }
    return 0;
}

T3 是个神仙题目 没读懂题目 随便写了个dij 预处理上下左右 样例都没过 然后交了 有5分??

我以为 每个点都能丢传送门 然后不用考虑那么多 我就暴力想相邻四个格子连边 然后向 上下左右能拓展的地方连边 此时 我把边权设置为了1 其实边权应该是离他最近的箱子的距离

当时并没有读懂题目 然后设置成了1 不过后来稍微改一下即可 

我们可以向四个方向连边 边权是1 然后向四周所能扩展到的最远距离 连边 边权是离这个点最近的障碍的距离 有人bfs一遍 预处理了这个东西 但是没有必要

维护l r up down 数组的时候 可以直接取min 找到最近的点 预处理的方式和 悬线法的预处理是一样的qwq

#include<bits/stdc++.h>
using namespace std;
template<typename T>inline void read(T &x) {
    x=0;T f=1,ch=getchar();
    while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    x*=f;
}
typedef pair<int,int> PII;
const int N=1e3+10;
struct gg {
    int y,next,v;
}a[N*N<<3];
int n,m,tot,s,t,lin[N*N],mark[1100][1100],vis[N*N],dis[N*N];
int l[1100][1100],r[1100][1100],up[1100][1100],down[1100][1100];
char ch[1100][1100];
inline void add(int x,int y,int v) {
    a[++tot].y=y;
    a[tot].next=lin[x];
    lin[x]=tot;
    a[tot].v=v; 
}
inline int num(int x,int y) {return (x-1)*m+y;}
inline int dij(int s) {
    priority_queue<PII,vector<PII>,greater<PII> >q;
    memset(dis,0x3f,sizeof(dis));
    q.push(make_pair(0,s));dis[s]=0;
    while(q.size()) {
        int x=q.top().second;q.pop();
        if(vis[x]) continue;
        vis[x]=1;
        if(x==t) return dis[x]; 
        for(int i=lin[x];i;i=a[i].next) {
            int y=a[i].y;
            if(dis[y]>dis[x]+a[i].v) {
                dis[y]=dis[x]+a[i].v;
                q.push(make_pair(dis[y],y));
            }
        }
    }
    return -1;
}
inline void pre() {
    for(int i=1;i<=n;i++) {
        l[i][0]=0;
        for(int j=1;j<=m;j++) {
            l[i][j]=!mark[i][j]?j:l[i][j-1];
        }
        r[i][m+1]=m+1;
        for(int j=m;j>=1;j--) {
            r[i][j]=!mark[i][j]?j:r[i][j+1];
        }
    }
    for(int j=1;j<=m;j++) {
        up[0][j]=0;
        for(int i=1;i<=n;i++) {
            up[i][j]=!mark[i][j]?i:up[i-1][j];
        }
        down[n+1][j]=n+1;
        for(int i=n;i>=1;i--) {
            down[i][j]=!mark[i][j]?i:down[i+1][j];
        }
    }
}
inline void edge() {
    for(int i=1;i<=n;i++) {
        for(int j=1;j<=m;j++) {
            if(!mark[i][j]) continue;
            if(i+1<=n&&mark[i+1][j]) {
                add(num(i,j),num(i+1,j),1);
                add(num(i+1,j),num(i,j),1);
            }
            if(j+1<=m&&mark[i][j+1]) {
                add(num(i,j),num(i,j+1),1);
                add(num(i,j+1),num(i,j),1);
            }
        }
    }
    for(int i=1;i<=n;i++) {
        for(int j=1;j<=m;j++) {
            if(!mark[i][j]) continue;
            int dl=j-l[i][j];
            int dr=r[i][j]-j;
            int dup=i-up[i][j];
            int ddown=down[i][j]-i;
            int w=min(min(dl,dr),min(dup,ddown));    
            if(l[i][j]!=j-1) add(num(i,j),num(i,l[i][j]+1),w);
            if(r[i][j]!=j+1) add(num(i,j),num(i,r[i][j]-1),w);
            if(up[i][j]!=i-1) add(num(i,j),num(up[i][j]+1,j),w);
            if(down[i][j]!=i+1) add(num(i,j),num(down[i][j]-1,j),w);
        }
    }
}
int main() {
//    freopen("esc.in","r",stdin);
//    freopen("esc.out","w",stdout); 
    read(n); read(m);
    for(int i=1;i<=n;i++) {
        for(int j=1;j<=m;j++) {
            cin>>ch[i][j];
            if(ch[i][j]=='S') s=num(i,j); 
            if(ch[i][j]=='C') t=num(i,j);
            if(ch[i][j]=='#') mark[i][j]=0;
            else mark[i][j]=1;
        }
    }
    pre();
    edge();
    printf("%d\n",dij(s));
    return 0;
}

 

 

 

 

posted @ 2019-11-03 22:04  Tyouchie  阅读(273)  评论(0编辑  收藏  举报