- 上午
- BZOJ 1029 [JSOI2007]建筑抢修
贪心,按t2排序后
依次枚举建筑,尝试对其进行修理。
如果当期建筑无法修理,
则看之前是否有某个已经修理了的建筑的耗时大于当期建筑的耗时,
如果有的话,则替换掉最大的那一个。
优先队列维护
代码:
#include<queue> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define ll long long using namespace std; struct construction{ ll t1,t2; bool operator <(const construction &rtm) const{ return t2<rtm.t2; } }con[150005]; ll used,cnt; int n; priority_queue<ll>q; int main(){ scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%lld%lld",&con[i].t1,&con[i].t2); sort(con+1,con+n+1); for(int i=1;i<=n;i++){ ll ned=con[i].t1,lim=con[i].t2; if(used+ned>lim){ if(q.empty()) continue; if(q.top()<ned) continue; used=used-q.top()+ned; q.pop(); q.push(ned); } else{ used=used+ned; cnt++; q.push(ned); } } printf("%lld",cnt); return 0; }
- 1030 [JSOI2007]文本生成器
终于接触了AC自动机上的DP,好激动。
按网上大佬的思路,我们要求的是不合法的串的个数。
然后对模式串建trie树,构造失配指针,
把所有不存在的边补上,避免反复沿着失配边走(---刘汝佳)
接下来进行DP,对每个位置枚举填的是trie树的哪个节点,对于有结尾标记d的节点就不转移。
给我的感觉是,这个getFail函数和"补边"操作呢,就是在处理DP的转移方向。
代码:
#include<queue> #include<cstdio> #include<cstring> #include<iostream> using namespace std; const int p=10007; char s[105]; bool ed[60005]; int ch[60005][30],fail[60005],sz; int dp[105][6005],all=1,ans,n,m; int idx(char x){ return x-'A'+1; } void insert(){ int u=0,j=0; while(s[j]!=0){ if(!ch[u][idx(s[j])]) ch[u][idx(s[j])]=++sz; u=ch[u][idx(s[j])]; j++; } ed[u]=1; } void getFail(){ queue<int>q; for(int c=1;c<=26;c++)if(ch[0][c]) q.push(ch[0][c]); while(!q.empty()){ int u=q.front(); q.pop(); for(int c=1;c<=26;c++){ if(!ch[u][c]){ ch[u][c]=ch[fail[u]][c]; continue; } int v=ch[u][c]; fail[v]=ch[fail[u]][c]; if(ed[fail[v]]) ed[v]=1; q.push(v); } } } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%s",s),insert(); for(int i=1;i<=26;i++) if(!ch[0][i]) ch[0][i]=++sz; getFail(); dp[0][0]=1; for(int i=1;i<=m;i++) for(int j=0;j<=sz;j++){ if(ed[j]||!dp[i-1][j]) continue; for(int k=1;k<=26;k++) dp[i][ch[j][k]]=(dp[i][ch[j][k]]+dp[i-1][j])%p; } for(int i=1;i<=m;i++) all=all*26%p; for(int j=1;j<=sz;j++) if(!ed[j]) ans=(ans+dp[m][j])%p; ans=(all-ans+p)%p; printf("%d ",ans); return 0; }
- BZOJ 1031 [JSOI2007]字符加密Cipher
这是一道裸的后缀数组!(以前没过,今天终于AC了)
代码:
#include<cstdio> #include<cstring> #include<iostream> #define MAXN 200005 using namespace std; char s[MAXN]; int wa[MAXN],wb[MAXN]; int sa[MAXN],c[MAXN],n; bool cmp(int *y,int i,int k){ int aa=y[sa[i]],bb=y[sa[i-1]]; int cc=sa[i]+k<n?y[sa[i]+k]:-1,dd=sa[i-1]+k<n?y[sa[i-1]+k]:-1; return aa==bb&&cc==dd; } void build(){ int m=300,*x=wa,*y=wb; for(int i=0;i<m;i++) c[i]=0; for(int i=0;i<n;i++) c[x[i]=s[i]]++; for(int i=1;i<m;i++) c[i]+=c[i-1]; for(int i=n-1;i>=0;i--) sa[--c[x[i]]]=i; for(int k=1;k<n;k<<=1){ int p=0; for(int i=n-k;i<n;i++) y[p++]=i; for(int i=0;i<n;i++) if(sa[i]>=k) y[p++]=sa[i]-k; for(int i=0;i<m;i++) c[i]=0; for(int i=0;i<n;i++) c[x[y[i]]]++; for(int i=1;i<m;i++) c[i]+=c[i-1]; for(int i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i]; m=1; swap(x,y); x[sa[0]]=0; for(int i=1;i<n;i++) x[sa[i]]=cmp(y,i,k)?m-1:m++; if(m>=n) break; } } int main(){ scanf("%s",s); int N=strlen(s); for(int i=0;i<N;i++) s[i+N]=s[i]; n=N*2; s[n]=0; build(); for(int i=0;i<n;i++) if(sa[i]<N) printf("%c",s[sa[i]+N-1]); return 0; }
- BZOJ 1034 [ZJOI2008]泡泡堂BNB
从小到大排序后
用了一个优先队列维护之前平局的选手实力、、、
对于从小到大枚举到的对手,
尝试找出当前可以干掉他的最小队友,
对于干不掉他的对友,尝试去干掉优先队列维护的平局的实力最小的对手,可以再多加一分。
感觉麻烦了,网上的更简单。
代码:
#include<queue> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; int a[100005],b[100005]; int n; int work(int *A,int *B){ priority_queue<int,vector<int>,greater<int> >q; int ans=0,i=1; for(int j=1;j<=n;j++){ while(i<=n&&A[i]<=B[j]){ if(!q.empty()&&q.top()<A[i]) q.pop(),ans+=1; else if(A[i]==B[j]) break; i++; } if(i>n) break; if(A[i]==B[j]) ans+=1,q.push(B[j]); else ans+=2; i++; } return ans; } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<=n;i++) scanf("%d",&b[i]); sort(a+1,a+n+1); sort(b+1,b+n+1); int ans1=work(a,b); int ans2=2*n-work(b,a); printf("%d %d",ans1,ans2); return 0; }
- 下午
- 考初赛、、、