10.10&&10.11 模拟赛

不清楚题目会不会越来越毒瘤 昨天T2 写两个小时 今天T2 写了 将近三个小时 而且两个都是最短路 和 状压dp 难道今年状压 高频考点??

反正 有点恶心的 是其中的 一些细节 需要思考到 不然会出现 很多问题 导致考场上 崩溃 我考场上已经将近崩溃了 然后T3 没写 最后发现T3 难度小于T2 

10.10  100+100+0

这次有两个ak神仙 其实我T3 应该写的 不过当时没时间了 T2 浪费时间太多了 

T1 有个贪心就是 排序之后每次删除最大的  其实考虑到 在删除点 实际上上一条一条删除边 那么 肯定会 每次选择一条边的最小值 删除 所以枚举所有的边即可

其实 当时发现是两个最小值相加 代码很短 于是试了试 发现可以过大样例 于是简单证明了 所以还是很水的8 大部分同学都A了T1

//暴力观察 你会发现 删点相当于是一条一条删边的 那么每次删除 考虑贪心 一定是两个点之间做抉择 选择最小的 
//直接枚举 所有的边 累加最小值 这样是正确吗 有待思考.... 
//正确性是显然的 
#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;
const int N=100010;
int n,m,x,y,a[N];
ll ans;
int main() {
    //freopen("1.in","r",stdin);
    freopen("dt.in","r",stdin);
    freopen("dt.out","w",stdout); 
    read(n); read(m);
    for(int i=1;i<=n;i++) read(a[i]);
    for(int i=1;i<=m;i++) {
        read(x); read(y);
        ans+=min(a[x],a[y]);
    }
    printf("%lld
",ans); 
    return 0;
}
View Code

 T2 状压dp 然后bfs (n) 预处理最短路

其实我发现这个题解太秀了 竟然让你根据每个特殊测试点 写算法 不过这确实考场上的策略 这样T2 至少能有70分 但是 我看到部分同学没有拿到这个分数

 

 其实这个特殊数据真的给的很良心了qwq 不过我不再分析部分分的做法了 直接 讲dp

考虑 数据范围之后 我们进行状压 此时 要跑最短路 那么 我们选择bfs 而不是dij  针对网格图 那么每一个点的膨胀度实际上是可以预处理出来的 s<=10 所以怎么判断都可以

然后考虑此时 $dis[i][j][k]$表示从i这个菜市到达(j,k) 这个点的最短路 $pzdd[i][j][k]$表示从i到达(j,k) 的碰撞度之和 $f[i][j][0]$ 表示从当前的状态集合是i 然后到达 j 的最短路(0) 最大膨胀度之和(1)

转移的时候 注意双关键字更新dp 状态转移的时候 转移减去pzd 的 重复贡献即可 其实这种细节应该注意到的

注意我代码中对f数组的初始化 实际上是不严谨的 很容易出锅

其实 对于最短路的部分 我们初始化正无穷 对于 最大碰撞度之和 其实是不应该初始化为正无穷的 不太严谨

//显然今天的T2 和 昨天的 T2 撞了
//dij还是要写的 状压也是要写的 p<=15 s<=10 那么考虑怎么dp呢 有待思考
//网格图 应该bfs好一点 dij可能被卡
//比较恶心的 一定 保证是在 最短路的 前提下 再更新最大体积和
//所以更新最短路的同时就要更新体积 比较最短路 相等 那么 更新 最大体积 
#include<bits/stdc++.h>
#define ll long long
#define rint register int
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 dx[4]={0,1,0,-1};
const int dy[4]={1,0,-1,0};
const int inf=1<<30;
struct pink {
    int x,y;
}ss[21];
int n,m,s,a[305][305],sum[305][305],pzd[305][305],f[21][(1<<15)+1000][2];
int dis[21][305][305],pzdd[21][305][305],vis[305][305],st,ed,p;
int res1=inf,res2=-inf;
//f[i][j][0]表示在 状态j 的到达 i 的最短距离 
//f[i][j][1]表示在状态j的 到达 i 的 最大膨胀度之和 
//dis[i][j][k]表示从第i个菜场 到 (j,k) 这个点的最短距离; 
inline int ask(int x1,int y1,int x2,int y2) {
    return sum[x2][y2]-sum[x1-1][y2]-sum[x2][y1-1]+sum[x1-1][y1-1];
}
void get_pzd(int x,int y) {
    for(int k=s;k>=0;k--) {
        if(x-k<1||x+k>n||y-k<1||y+k>m) continue;
        if(ask(x-k,y-k,x+k,y+k)==0)  {
            pzd[x][y]=k; 
            return;
        }
    }
    return;
}
inline void bfs(int num,int sx,int sy) {
    queue<pink> q;
    while(q.size()) q.pop();
    pink h,w;
    h.x=sx,h.y=sy;
    q.push(h);
    dis[num][sx][sy]=0; pzdd[num][sx][sy]=pzd[sx][sy];//初始化wa 
    memset(vis,0,sizeof(vis)); 
    vis[sx][sy]=1;
    while(q.size()) {
        pink tmp=q.front();
        int x=tmp.x,y=tmp.y; q.pop();
        for(int i=0;i<4;i++) {
            int xx=x+dx[i];
            int yy=y+dy[i];
            if(xx<1||yy<1||xx>n||yy>m) continue;
            if(a[xx][yy]) continue;
            if(!vis[xx][yy]) {
                vis[xx][yy]=1;
                dis[num][xx][yy]=dis[num][x][y]+1;
                pzdd[num][xx][yy]=pzdd[num][x][y]+pzd[xx][yy];
                w.x=xx,w.y=yy;
                q.push(w);
            }
            else {
                if(dis[num][xx][yy]==dis[num][x][y]+1) {
                      pzdd[num][xx][yy]=max(pzdd[num][xx][yy],pzdd[num][x][y]+pzd[xx][yy]);
//                      cout<<pzdd[num][xx][yy];
                }
            }
        }
    }
}
int main() {
    freopen("1.in","r",stdin);
//    freopen("expand.in","r",stdin);
//    freopen("expand.out","w",stdout);
    read(n); read(m); read(s);
    for(rint i=1;i<=n;i++) {
        for(rint j=1;j<=m;j++) {
            read(a[i][j]);
          }
    }
    for(rint i=1;i<=n;i++) {
        for(rint j=1;j<=m;j++) {
            sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j];
          }
    }    
    /*for(int i=1;i<=n;i++) {
        for(int j=1;j<=m;j++) {
            cout<<sum[i][j]<<' ';
        } 
        cout<<endl;
    }*/
    for(rint i=1;i<=n;i++) {
          for(rint j=1;j<=m;j++) { 
              if(a[i][j]==1) {
                  pzd[i][j]=-inf;
                continue;
              }
              get_pzd(i,j);
          //cout<<w[i][j]<<' ';
        }
        //cout<<endl;
    }
    /*for(int i=1;i<=n;i++) {
        for(int j=1;j<=m;j++) {
            cout<<w[i][j]<<' ';
        }
        cout<<endl;
    }*/
    for(rint i=1;i<=p;i++) {
         for(rint j=1;j<=n;j++) {
              for(rint z=1;z<=m;z++) {
                  dis[i][j][z]=1e9;
                  pzdd[i][j][z]=-1e9;
            }
          }
    }
    read(st); read(ed); st++; ed++;
    read(p);
    for(rint i=1;i<=p;i++) {
          read(ss[i].x); read(ss[i].y);
          ss[i].x++; ss[i].y++;
          bfs(i,ss[i].x,ss[i].y);
    }
    memset(f,0x3f,sizeof(f));//会出锅 
    for(int i=1;i<=p;i++) {
          f[i][1<<(i-1)][0]=dis[i][st][ed];
          f[i][1<<(i-1)][1]=pzdd[i][st][ed];
//          cout<<pzdd[i][st][ed]<<endl;
    } 
    for(int i=1;i<(1<<p);i++) {
          for(int j=1;j<=p;j++) {
            if(i&(1<<(j-1))) {//j
                for(int z=1;z<=p;z++)
                  if(!(i&(1<<(z-1)))) {
                      if(f[j][i][0]+dis[j][ss[z].x][ss[z].y]<f[z][i|(1<<(z-1))][0]) {
                            f[z][i|(1<<(z-1))][0]=f[j][i][0]+dis[j][ss[z].x][ss[z].y];
                        f[z][i|(1<<(z-1))][1]=f[j][i][1]+pzdd[j][ss[z].x][ss[z].y]-pzd[ss[j].x][ss[j].y];//减去重复的; 
                      }
                      if(f[j][i][0]+dis[j][ss[z].x][ss[z].y]==f[z][i|(1<<(z-1))][0]) {
                            f[z][i|(1<<(z-1))][1]=max(f[z][i|(1<<(z-1))][1],
                        f[j][i][1]+pzdd[j][ss[z].x][ss[z].y]-pzd[ss[j].x][ss[j].y]);//减去重复的pzd wa2 
                      }                                                                          
                  }
            }
        }
    }
    for(int i=1;i<=p;i++) { 
       if(f[i][(1<<p)-1][0]<res1)  {
               res2=f[i][(1<<p)-1][1];
               res1=f[i][(1<<p)-1][0];
       }
       if(f[i][(1<<p)-1][0]==res1) res2=max(res2,f[i][(1<<p)-1][1]);
    }
    printf("%d %d",res1,res2);
    return 0;
}
View Code

T3 dij 跑最短路 同时dp 更新 

很容易想到 状态 $dp[i][j]$ 表示从起点到 i 这个点 当前gcd为 j 的最短路 

那么转移的时候 求一下所有出边的gcd 然后取min 即可 但是 我们发现 对于所有合法的状态 只在g'|w 的时候进行转移 所以我们对于一条边 我们枚举当前gcd 的倍数 更新状态

然后还有一个优化 我们注意到n只有1000 但是询问有1e5 所以我们dij 最多1000遍 但是还是有点麻烦 所以 我们注意到 终点是不变的 所以我们考虑到从终点开始dij 只用一遍就行了

但是 我这里写了1000 遍dij 水过了此题

10.11 这次模拟赛很爆炸啊qwq  有点难啊

T1 不行了啥鬼畜东西啊 

第一遍 设当前存在p个a m个b q个c 然后pa+mb+qc=n 咋了这是让我解所有方程所有解得吗

懵了 当时 T1就被磕住 其实考场上有点崩溃吧 后来考虑50分 肯定是dp啊 f[i][0/1] 表示当前的位置是男(0) 的时间 是女(1) 的时间 考虑此时转移 要记录每个点 是男/女的方案数

然后转移很显然

果断放弃T1 去写了T2

#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;
}
int n,a,b,c,d,e;
ll f[1000100][2];
ll num[1000100][2];
const int mod=1000000007;
int main() {
    freopen("queue.in","r",stdin);
    freopen("queue.out","w",stdout);
    read(n); read(a); read(b); read(c); read(d); read(e);
    num[0][1]=num[0][0]=1;
    f[0][1]=d,f[0][0]=e;
    for(int i=1;i<=n;i++) {
        if(i>=a) {
            f[i][1]=(f[i][1]+f[i-a][1]+num[i-a][1]*d)%mod;
            num[i][1]=(num[i][1]+num[i-a][1])%mod;
        }
        if(i>=b) {
            f[i][1]=(f[i][1]+f[i-b][0]+num[i-b][0]*d)%mod;
            num[i][1]=(num[i][1]+num[i-b][0])%mod;
            f[i][0]=(f[i][0]+f[i-b][1]+num[i-b][1]*e)%mod;
            num[i][0]=(num[i][0]+num[i-b][1])%mod;
        }
        if(i>=c) {
            f[i][0]=(f[i][0]+f[i-c][0]+num[i-c][0]*e)%mod;
            num[i][0]=(num[i][0]+num[i-c][0])%mod;
        }
    }
    cout<<(f[n][1]+f[n][0])%mod<<endl;
    return 0;
}
View Code

正解是个是个矩阵递推 其实可以发现的 因为国庆当时ZR考试T1 就是矩阵 数据都是1e18 感觉下次要敏感一点了 没写代码 我列不出来这个矩阵 120*120呜呜呜

//#include<bits/stdc++.h>
#include<iostream>
#include<queue>
#include<iomanip>
#include<cctype>
#include<cstdio>
#include<deque>
#include<utility>
#include<cmath>
#include<ctime>
#include<cstring>
#include<string>
#include<cstdlib>
#include<vector>
#include<algorithm>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)>(y)?(y):(x))
#define INF 1000000000
#define ll long long
#define db double
#define mod 1000000007
#define pii pair<ll,ll>
#define mk make_pair
using namespace std;
char buf[1<<15],*fs,*ft;
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline ll read()
{
    ll x=0,f=1;char ch=getc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
    return x*f;
}
const ll MAXN=121;
ll n,m;
ll maxx,maxx1,maxx2;
ll a,b,c,d,e;
ll f[MAXN][2],g[MAXN][2];
struct wy
{
    ll w[MAXN];
    ll c[MAXN][MAXN];
    wy(){memset(w,0,sizeof(w));memset(c,0,sizeof(c));}
    friend wy operator *(wy C,wy D)
    {
        wy tmp;
        for(ll i=1;i<=m;++i)tmp.w[i]=C.w[i];
        for(ll i=1;i<=m;++i)
            for(ll j=1;j<=m;++j)
                for(ll k=1;k<=m;++k)
                    tmp.c[i][j]=(tmp.c[i][j]+C.c[i][k]*D.c[k][j])%mod;
        return tmp;
    }
    friend wy operator -(wy C,wy D)
    {
        wy tmp;
        for(ll i=1;i<=m;++i)
            for(ll j=1;j<=m;++j)
                tmp.c[i][j]=C.c[i][j];
        for(ll i=1;i<=m;++i)
            for(ll j=1;j<=m;++j)
                tmp.w[i]=(tmp.w[i]+C.w[j]*D.c[j][i])%mod;
        return tmp;
    }
    friend wy operator ^(wy A,ll p)
    {
        wy tmp=A;
        while(p)
        {
            if(p&1)tmp=tmp-A;
            p=p>>1;
            A=A*A;
        }
        return tmp;
    }
}A;
inline void get_pre(ll x)
{
    f[0][0]=d;f[0][1]=e;
    g[0][0]=1;g[0][1]=1;
    for(ll i=1;i<=x;++i)
    {
        if(i-a>=0)
        {
            f[i][0]=(f[i][0]+f[i-a][0]+d*g[i-a][0])%mod;
            g[i][0]=(g[i][0]+g[i-a][0])%mod;
        }
        if(i-b>=0)
        {
            f[i][0]=(f[i][0]+f[i-b][1]+d*g[i-b][1])%mod;
            g[i][0]=(g[i][0]+g[i-b][1])%mod;
        }
        if(i-c>=0)
        {
            f[i][1]=(f[i][1]+f[i-c][1]+e*g[i-c][1])%mod;
            g[i][1]=(g[i][1]+g[i-c][1])%mod;
        }
        if(i-b>=0)
        {
            f[i][1]=(f[i][1]+f[i-b][0]+e*g[i-b][0])%mod;
            g[i][1]=(g[i][1]+g[i-b][0])%mod;
        }
    }
}
int main()
{
    //freopen("1.in","r",stdin);
    freopen("queue.in","r",stdin);
    freopen("queue.out","w",stdout);     
    n=read();
    a=read();b=read();c=read();d=read();e=read();
    maxx1=max(a,b);maxx2=max(b,c);
    maxx=max(maxx1,maxx2);
    get_pre(maxx);
    
    for(ll i=1;i<=maxx;++i)A.w[i]=g[i][0];
    A.c[maxx-a+1][maxx]=1;A.c[2*maxx-b+1][maxx]=1;
    for(ll j=maxx-1;j;--j)A.c[j+1][j]=1;
    
    for(ll i=maxx+1;i<=2*maxx;++i)A.w[i]=g[i-maxx][1];
    A.c[2*maxx-c+1][2*maxx]=1;A.c[maxx-b+1][2*maxx]=1;
    for(ll j=maxx*2-1;j>=maxx+1;--j)A.c[j+1][j]=1;
    
    for(ll i=maxx*2+1;i<=maxx*3;++i)A.w[i]=f[i-maxx*2][0];
    A.c[3*maxx-a+1][maxx*3]=1;A.c[maxx-a+1][maxx*3]=d;
    A.c[4*maxx-b+1][maxx*3]=1;A.c[2*maxx-b+1][maxx*3]=d;
    for(ll i=maxx*3-1;i>=maxx*2+1;--i)A.c[i+1][i]=1;
    
    for(ll i=maxx*3+1;i<=maxx*4;++i)A.w[i]=f[i-maxx*3][1];
    A.c[maxx*4-c+1][maxx*4]=1;A.c[2*maxx-c+1][maxx*4]=e;
    A.c[maxx*3-b+1][maxx*4]=1;A.c[maxx-b+1][4*maxx]=e;
    for(ll i=maxx*4-1;i>=maxx*3+1;--i)A.c[i+1][i]=1;
    
    //printf("%lld
",A.w[maxx]);
    //printf("%lld
",A.w[2*maxx]);
    
    m=maxx<<2;A=A^(n-maxx);
    
    //printf("%lld
",A.w[maxx]);
    //printf("%lld
",A.w[2*maxx]);
    
    printf("%lld
",(A.w[maxx*3]+A.w[maxx*4])%mod);
    return 0;
}
View Code

T2 多模式串匹配多文本串 KMP 不行 AC自动机 不行

考虑这个时候出现了题目中的lcp 我丢 难道是后缀数组 O(1) 求两个字符串后缀的lcp 考虑建出来后缀数组 nlogn 然后查询 n^2 还是不行 而且我还不会后缀数组

然后算法就比较好想啊 trie 直接上啊

但是我写挂了qwq 自闭 

当时根本就没想到 把id 和 名字全部插入 trie 然后标记  当时只插入了一个 然后贪心找了

其实正解也算是贪心吧 考虑都插入trie 中 标记一下 是id 还是名字 从叶子向上搞 此时发现 如果有个点是lca 那么肯定直接匹配 累加方案数即可

放个std 电脑重启 代码不知道去哪了 不想再写了 意念写过了

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
typedef long long LL;

const int N=2e6+10;

int n;
char A[N];
LL ans;

struct Trie
{
    int son[N][26],cnt[N][2],node;
    
    int newnode()
    {
        ++node;
        for (int i=0;i<26;++i) son[node][i]=0;
        cnt[node][0]=cnt[node][1]=0;
        return node;
    }
    
    void Insert(char *A,int v)
    {
        int p=0,len=strlen(A);
        for (int i=0;i<len;++i)
        {
            int &x=son[p][A[i]-'a'];
            p=x?x:x=newnode();
        }
        cnt[p][v]++;
    }
    
    void Dfs(int x,int dep)
    {
        for (int i=0;i<26;++i)
            if (son[x][i])
            {
                Dfs(son[x][i],dep+1);
                cnt[x][0]+=cnt[son[x][i]][0];
                cnt[x][1]+=cnt[son[x][i]][1];
            }
        int k=min(cnt[x][0],cnt[x][1]);
        ans+=1ll*dep*k;
        cnt[x][0]-=k; cnt[x][1]-=k;
    }
    
}Tr;

void Init()
{
    scanf("%d",&n);
    for (int i=1;i<=n;++i)
    {
        scanf("%s",A);
        Tr.Insert(A,0);
    }
    
    for (int i=1;i<=n;++i)
    {
        scanf("%s",A);
        Tr.Insert(A,1);
    }
}

void Solve()
{
    Tr.Dfs(0,0);
    printf("%lld
",ans);
}

int main()
{
    freopen("choose.in","r",stdin);
    freopen("choose.out","w",stdout);
    Init();
    Solve();
    fclose(stdin);
    fclose(stdout);
    return 0;
}
View Code

T3  算了根本没搜出来 感谢出题人 送的十分呜呜呜

原文地址:https://www.cnblogs.com/Tyouchie/p/11655049.html