12.8 模拟赛
T1 成绩单 bzoj 4897
题目大意:
一个数列 每次可以取出连续的一段 剩下的部分自动拼接起来 直到把序列取空 代价为:
$A \times k + B \times \sum_{i=1}^k {(max_i-min_i)^2}$
求最小代价
思路:
区间$dp_{i,j,a,b}$表示把区间$[i,j]$取到剩的数在$[a,b]$之间的代价
转移的时候把区间向里缩使得被缩的数在$[a,b]$之间 在剩余的区间中枚举断点
从两边取完或者剩$[a,b]$的状态转移过来
1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cmath> 5 #include<algorithm> 6 #include<cstring> 7 #include<vector> 8 #include<queue> 9 #include<complex> 10 #include<map> 11 #define rep(i,s,t) for(register int i=(s);i<=(t);++i) 12 #define dwn(i,s,t) for(register int i=(s);i>=(t);--i) 13 #define ren for(register int i=fst[x];i;i=nxt[i]) 14 #define Fill(x,t) memset(x,t,sizeof(x)) 15 #define ll long long 16 #define Cd complex<double> 17 #define inf 2139062143 18 #define MOD 998244353 19 #define MAXN 100100 20 #define MAXM 500100 21 using namespace std; 22 inline int read() 23 { 24 int x=0,f=1;char ch=getchar(); 25 while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();} 26 while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();} 27 return x*f; 28 } 29 int n,A,B,dp[60][60][60][60],g[MAXN],h[MAXN]; 30 int ok(int x,int l,int r) {return x>=l&&x<=r;} 31 int add(int x,int y) {return x==inf&&y==inf?inf:x+y;} 32 void upd(int &x,int y) {x=min(x,y);} 33 int main() 34 { 35 n=read(),A=read(),B=read();rep(i,1,n) g[i]=h[i]=read();Fill(dp,127); 36 sort(h+1,h+n+1);int l,r,m=unique(h+1,h+n+1)-h-1;rep(i,1,n) g[i]=lower_bound(h+1,h+m+1,g[i])-h; 37 rep(i,0,n) rep(j,1,n-i) rep(a,1,m) rep(b,a,m) 38 { 39 l=j,r=i+j;while(ok(g[l],a,b)) l++;while(ok(g[r],a,b)) r--; 40 if(l>r) dp[j][i+j][a][b]=0;else if(l==r) dp[j][i+j][a][b]=A; 41 else 42 { 43 rep(k,l,r-1) upd(dp[j][i+j][a][b],add(min(dp[l][k][a][b],dp[l][k][0][0]),min(dp[k+1][r][0][0],dp[k+1][r][a][b]))); 44 upd(dp[j][i+j][a][b],dp[l][r][0][0]); 45 } 46 if(dp[j][i+j][a][b]!=inf) upd(dp[j][i+j][0][0],dp[j][i+j][a][b]+A+B*(h[b]-h[a])*(h[b]-h[a])); 47 } 48 printf("%d\n",dp[1][n][0][0]); 49 }
T2 补退选 bzoj 4896
题目大意:
n行,每行描述一个事件;每行第一个正整数k表示事件类型:
1.如果k=1,表示选课事件,接下来一个字符串S,表示一个姓名为S的学生选了X老师的课
2.如果k=2,表示退课事件,接下来一个字符串S,表示一个姓名为S的学生退了X老师的课
3.如果k=3,表示查询事件,接下来一个字符串S以及三个非负整数a,b,c,表示X老师想知道最早在第几个事件之后,姓名以S为前缀的学生数量超过了给定的值
思路:
直接使用trie树大力模拟 对每个点开一个vector在上面二分即可
1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cstring> 5 #include<cmath> 6 #include<algorithm> 7 #include<queue> 8 #include<vector> 9 #include<map> 10 #include<set> 11 #define ll long long 12 #define inf 2139062143 13 #define rep(i,s,t) for(register int i=(s),i__end=(t);i<=i__end;i++) 14 #define dwn(i,s,t) for(register int i=(s),i__end=(t);i>=i__end;i--) 15 #define ren(x) for(register int i=fst[x];i;i=nxt[i]) 16 #define MAXN 100100 17 using namespace std; 18 inline int read() 19 { 20 int x=0,f=1;char ch=getchar(); 21 while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();} 22 while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();} 23 return x*f; 24 } 25 int n,tr[MAXN<<6][10],sz,sum[MAXN<<6],las[MAXN<<6]; 26 struct node{int id,mx;}; 27 vector <node> vec[MAXN<<6]; 28 char ch[65]; 29 void ins(int x,int val) 30 { 31 int pos=0,len=strlen(ch+1); 32 rep(i,1,len) 33 { 34 if(!tr[pos][ch[i]-'a']) tr[pos][ch[i]-'a']=++sz; 35 pos=tr[pos][ch[i]-'a'],sum[pos]+=val;vec[pos].push_back((node){x,max(sum[pos],las[pos])}); 36 las[pos]=max(sum[pos],las[pos]); 37 } 38 } 39 int query(int x) 40 { 41 int pos=0,len=strlen(ch+1); 42 rep(i,1,len) {pos=tr[pos][ch[i]-'a'];if(!pos) return -1LL;} 43 int l=0,mid,r=vec[pos].size()-1,res=-1; 44 while(l<=r) {mid=(l+r)>>1;if(vec[pos][mid].mx>x) res=mid,r=mid-1;else l=mid+1;} 45 return res<0?-1:vec[pos][res].id; 46 } 47 int main() 48 { 49 n=read();int t;ll a,b,c,las=0; 50 rep(i,1,n) 51 { 52 t=read();scanf("%s",ch+1); 53 if(t==1) ins(i,1); 54 else if(t==2) ins(i,-1); 55 else {a=read(),b=read(),c=read();a=(a*las+b)%c;printf("%lld\n",las=query(a));if(las<0) las=-las;} 56 } 57 } 58