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\n",ans); return 0; }
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; }
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; }
正解是个是个矩阵递推 其实可以发现的 因为国庆当时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\n",A.w[maxx]); //printf("%lld\n",A.w[2*maxx]); m=maxx<<2;A=A^(n-maxx); //printf("%lld\n",A.w[maxx]); //printf("%lld\n",A.w[2*maxx]); printf("%lld\n",(A.w[maxx*3]+A.w[maxx*4])%mod); return 0; }
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\n",ans); } int main() { freopen("choose.in","r",stdin); freopen("choose.out","w",stdout); Init(); Solve(); fclose(stdin); fclose(stdout); return 0; }
T3 算了根本没搜出来 感谢出题人 送的十分呜呜呜