模拟3
T3.tire树或者map映射,第一种高效10倍,待会再理解
#include<bits/stdc++.h> using namespace std; const int maxn = (1e4+10)*50; int tree[maxn][26],v[maxn][26],cnt[maxn][26],len,tot; char s[26]; void add(int l, int r, int id){ int rt = 0,t; while(l < r){//因为要看是否存在于字符串中而不是 是否为前缀 所以要把一个字符串以每个字符为起点存一次 t = s[l] - 'a';//将字母转化为对应的下标 if (tree[rt][t]) {//如果当前的字母已经建过节点 if (v[rt][t] != id) { v[rt][t] = id; ++cnt[rt][t];//当前字符串++ } } else {//如果还没有建过当前节点 tree[rt][t] = ++tot;//新建一个点 v[rt][t] = id; cnt[rt][t]++; } rt = tree[rt][t]; ++l; } } int search(){ int rt = 0,ans = 0; for(int i = 0;i < len;++i){ if(!tree[rt][s[i]-'a'])return 0;//如果当前字符未出现过 ans = cnt[rt][s[i]-'a'];//即为当前字符串出现的次数 rt = tree[rt][s[i]-'a'];//为下一次转移做准备 } return ans; } int main(){ freopen("mdz.in","r",stdin); freopen("mdz.out","w",stdout); int n;scanf("%d",&n); for(int i = 0;i < n;++i){ scanf("%s",s); len = strlen(s); for(int j = 0;j < len;++j){ add(j,len,i); } } int m;scanf("%d",&m); while(m--){ scanf("%s",s); len = strlen(s); printf("%d\n",search()); } }
C. SSY的队列 - 高一普及组模拟赛3 - 比赛 - 衡中OI (hszxoj.com)
方法:状态压缩+hash+dfs+记忆化 思路: 首先想到容斥原理,用所有的方案减去一个相同挨着的+两个相同挨着的,但是去重太麻烦,而且各个方案之间相互牵连,不方便实现
那么再仔细看题,可不可以对问题进行简化?
相邻的数差值是m的倍数,那全部摸上m,其实就是余数相同的不能放在一起
问题就抽象成了(3个red,5个blue,7个green排列相同颜色不挨着的方案数),深搜
dfs(dep,lst)第几个位置,上一个哪个种类,O(n^n)超时
发现,如果我知道当前lst组还剩下的人数,接下来还可以用的组别剩的人数分别有几组,我就可以从上面已求出状态转移过来
这个记忆化相当于大大减少了时复杂度O(n^n-相当多的一部分)
1处处特殊处理 const int N=40; const ll mod=1234567891,base=33; int a[N],b[N],c[N],mx,cnt; map<ll,ll>dp[N];//数组开不下 int n,m; ll fac[N]; inline ll dfs(int dep,int lst) { if(dep>n)return 1; memset(b,0,sizeof(b)); _f(i,1,cnt)//0不用算吧?至少不能一起算 { if(i!=lst)b[c[i]]++; } ll st=c[0];//b[i]=j,剩余人数是i的组有j个 _f(i,1,mx)//剩余人数是0个的组数我压根就没算 { st=st*base+b[i]; } st=st*base+((lst)?c[lst]:0);//表示对后边没有影响 if(dp[dep].count(st))return dp[dep][st];//开始统计 ll ans=0; if(c[0]) { c[0]--; ans+=dfs(dep+1,0); c[0]++; ans%=mod; } _f(i,1,cnt)//下一个位置放哪一类人 { if(i!=lst) { if(!c[i])continue; c[i]--; ans+=dfs(dep+1,i); c[i]++; ans%=mod; } } return dp[dep][st]=ans; } bool vis[N]; int main() { freopen("ssy.in","r",stdin); freopen("ssy.out","w",stdout); n=re(); _f(i,1,n) { a[i]=re(); } m=re(); _f(i,1,n) { a[i]%=m; if(a[i]<0)a[i]+=m;//负数的话摸出来也是负数,特殊加一下 } _f(i,1,n) { if(vis[i])continue; vis[i]=1; int ret=1; _f(j,i+1,n) { if(a[i]==a[j])vis[j]=1,ret++; } if(ret==1)c[0]++;//c[i]模数是相同的人分别有几个 else c[++cnt]=ret;//Cnt没有实际意义 mx=max(mx,ret); } fac[0]=1;ll ans=1; _f(i,1,n)fac[i]=fac[i-1]*i%mod; _f(i,0,cnt)ans=ans*fac[c[i]]%mod; ll po=dfs(1,0); ans=ans*po%mod; chu("%lld",ans); return 0; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | T4 法一:跑最短路,处理连边的时候倒着连0,就跑出来了(非常妙!!!) const int N=5e5+5; int a[N]; int n,m,ee; int tot; struct node { int to,nxt,w; }e[N<<2]; int head[N]; inline void add( int fr, int to, int va) { e[++tot].to=to,e[tot].nxt=head[fr],head[fr]=tot;e[tot].w=va; } queue< int >t; int dis[N],vis[N]; void dij() //从m-->e { dis[m]=0; t.push(m); int ans=inf; while (!t.empty()) { // chu("take out:%d\n",t.front()); int u=t.front(); t.pop(); vis[u]=0; for (R int i=head[u];i;i=e[i].nxt) { int v=e[i].to; //chu("%d to%d\n",u,v); if (dis[v]>dis[u]+e[i].w) { dis[v]=dis[u]+e[i].w; if (v>ee)ans=min(ans,dis[v]); // chu("dis[%d]:%d\n",v,dis[v]); if (!vis[v]) t.push(v); vis[v]=1; } } } if (ans==inf)chu( "-1" ); else chu( "%d" ,ans); } int main() { freopen( "clean.in" , "r" ,stdin); freopen( "clean.out" , "w" ,stdout); memset(dis,0x7f, sizeof (dis)); n=re(),m=re(),ee=re(); int maxi=ee+1,mii=m; _f(i,1,n) { int t1=re(),t2=re(),s=re(); add(t1,t2+1,s); maxi=max(maxi,max(t1,t2)); mii=min(mii,min(t1,t2)); } maxi=maxi+1; _f(i,mii,maxi) add(i,i-1,0); dij(); return 0; } 法二:线段树维护dp,区间最小值 f[i]=min(f[i],f[j]+cow[k]) //l[k]-1<=j<=r[k]-1 const int N=5e5+10; struct node { int ls,rs,data; }t[N<<4]; int tot=1; int n,m,e; void pushdown( int rt) { if (rt) { if (!lson)t[rt].data=t[rson].data; else if (!rson)t[rt].data=t[lson].data; else t[rt].data=min(t[lson].data,t[rson].data); } return ; } inline int merge( int rt, int l, int r, int pos, int d) { if (!rt){ rt=++tot; t[rt].data=inf; } if (l==r) { t[rt].data=min(t[rt].data,d); return rt; } int mid=(l+r)>>1; if (pos<=mid)t[rt].ls=merge(lson,l,mid,pos,d); else t[rt].rs=merge(rson,mid+1,r,pos,d); pushdown(rt); return rt; } inline int query( int rt, int l, int r, int ll, int rr) { if (!rt) return inf; if (ll<=l&&r<=rr) { return t[rt].data; } int ans=inf; int mid=(l+r)>>1; if (ll<=mid)ans=query(lson,l,mid,ll,rr); if (rr>mid)ans=min(ans,query(rson,mid+1,r,ll,rr)); return ans; } struct node2 { int t1,t2,s; bool operator <( const node2&a) const { return t2<a.t2; } }cow[N]; int f[N]; int lll=inf,rrr; int main() { freopen( "clean.in" , "r" ,stdin); freopen( "clean.out" , "w" ,stdout); n=re(),m=re()+2,e=re()+2; _f(i,1,n) { cow[i].t1=re()+2,cow[i].t2=re()+2,cow[i].s=re(); lll=min(lll,cow[i].t1); rrr=max(rrr,cow[i].t2); } sort(cow+1,cow+1+n); _f(i,m,rrr)f[i]=inf; merge(1,lll-1,rrr,m-1,0); _f(i,1,n) { int rr=cow[i].t2,ll=cow[i].t1; if (ll>e||rr<m) continue ; f[rr]=min(f[rr],query(1,lll-1,rrr,ll-1,rr-1)+cow[i].s); merge(1,lll-1,rrr,rr,f[rr]); } int ans=inf; _f(i,e,rrr) ans=min(ans,f[i]); if (ans==inf)chu( "-1" ); else chu( "%d" ,ans); } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】