现在才想起来写个解题报告。。。o(╯□╰)o
Day1:
T1:模拟题直接n^2乱搞。O(N^2)
T2:方法一:直接tarjan强连通分量。由于题目中图的特殊性质所以一个强连通分量就是一个环然后除了大小为1的强连通分量中最小的那个就是答案。
方法二:这是我考场上的做法。当时怕爆栈没敢写tarjan(Day2考到一半才告诉我栈空间为内存上限还有什么用吗QAQ),然后先写了个dfs求深度乱搞完挂。。浪费了20分钟。然后在纸头上写了些什么。首先找出连通块,每个连通块显然独立,现在对连通块进行操作。发现其实这个图有n个点n条边,所以肯定是一个环+一棵树连在一起。然后就用类似拓扑的方法不断去掉入度为0的点剩下来的肯定构成一个环了。显然这个环不会用节点往外延伸了因为每个点出度为1嘛。然后几个bfs乱搜O(N)搞定。这道题写了一个小时多一点。。
时间复杂度O(N)
T3:考场上我是先预处理哪些牌放一起可以一起出的。然后用二进制表示那张牌有没有出再记忆化。傻逼一样用了剩下的一个半小时调常数。本来写成双向宽搜可以骗85~90分的。
考后知道的一个比较好的方法。先枚举顺子,然后剩下的牌就是单牌,对子,三对,炸弹了,跟牌的点值一点关系没有。记忆化一下(预处理也可以)f[i][j][x][y]表示i个单牌,j个对子,x个三对,y个炸弹出完要几步。注意可以把大的拆成小的(比如一个炸弹拆成一张单牌和一个三对)。由于23张牌最多4个顺子,所以实际上是很快的。
还有注意一下王。如果单王直接看成单牌。否则既可以看成两张单牌,还可以先出火箭再出其他牌。
最多四个顺子,所以最多递归四层。每层3~A一个12张牌,枚举顺子O(12^2)复杂度。预处理O(N^4/4!)=O(N^4),总时间复杂度O(N^4+12^8)。
UPD:代码有误不能直接调用f[][][][]要调用dp()
AC代码:
- #include<iostream>
- #include<cstdio>
- #include<cstring>
- #define inf 1000000000
- using namespace std;
- int n,cas,ans,f[30][30][30][30],a[1005],tot[1005];
- int dp(int x,int y,int p,int q){
- if (x<0 || y<0 || p<0 || q<0) return inf;
- if (f[x][y][p][q]!=-1) return f[x][y][p][q];
- int tmp=inf;
- tmp=min(tmp,dp(x-1,y,p,q)+1); tmp=min(tmp,dp(x,y-1,p,q)+1);
- tmp=min(tmp,dp(x,y,p-1,q)+1); tmp=min(tmp,dp(x,y,p,q-1)+1);
- tmp=min(tmp,dp(x-1,y,p-1,q)+1); tmp=min(tmp,dp(x,y-1,p-1,q)+1);
- tmp=min(tmp,dp(x-2,y,p,q-1)+1); tmp=min(tmp,dp(x,y-2,p,q-1)+1);
- tmp=min(tmp,dp(x,y,p,q-2)+1); tmp=min(tmp,dp(x+2,y-1,p,q));
- tmp=min(tmp,dp(x+1,y+1,p-1,q)); tmp=min(tmp,dp(x+1,y,p+1,q-1));
- tmp=min(tmp,dp(x,y+2,p,q-1));
- return f[x][y][p][q]=tmp;
- }
- void work(int k){
- ans=min(ans,f[tot[1]][tot[2]][tot[3]][tot[4]]+k);
- if (a[14] && a[15]) ans=min(ans,f[tot[1]-2][tot[2]][tot[3]][tot[4]]+k+1);
- int i,j;
- for (i=1; i<=8; i++)
- if (a[i] && a[i+1] && a[i+2] && a[i+3] && a[i+4]){
- for (j=i; j<i+5; j++){
- tot[a[j]]--; a[j]--; tot[a[j]]++;
- }
- work(k+1);
- for (j=i+5; j<=12; j++){
- if (!a[j]) break;
- tot[a[j]]--; a[j]--; tot[a[j]]++;
- work(k+1);
- }
- for (j--; j>=i; j--){
- tot[a[j]]--; a[j]++; tot[a[j]]++;
- }
- }
- for (i=1; i<=10; i++)
- if (a[i]>1 && a[i+1]>1 && a[i+2]>1){
- for (j=i; j<i+3; j++){
- tot[a[j]]--; a[j]-=2; tot[a[j]]++;
- }
- work(k+1);
- for (j=i+3; j<=12; j++){
- if (a[j]<2) break;
- tot[a[j]]--; a[j]-=2; tot[a[j]]++;
- work(k+1);
- }
- for (j--; j>=i; j--){
- tot[a[j]]--; a[j]+=2; tot[a[j]]++;
- }
- }
- for (i=1; i<=11; i++)
- if (a[i]>2 && a[i+1]>2){
- for (j=i; j<i+2; j++){
- tot[a[j]]--; a[j]-=3; tot[a[j]]++;
- }
- work(k+1);
- for (j=i+2; j<=12; j++){
- if (a[j]<3) break;
- tot[a[j]]--; a[j]-=3; tot[a[j]]++;
- work(k+1);
- }
- for (j--; j>=i; j--){
- tot[a[j]]--; a[j]+=3; tot[a[j]]++;
- }
- }
- }
- int main(){
- memset(f,-1,sizeof(f)); f[0][0][0][0]=0;
- cas=dp(0,0,0,6); scanf("%d%d",&cas,&n);
- while (cas--){
- int i; ans=inf;
- memset(a,0,sizeof(a)); memset(tot,0,sizeof(tot));
- for (i=1; i<=n; i++){
- int x,y; scanf("%d%d",&x,&y);
- if (!x) a[y+13]++; else
- if (x<3) a[x+11]++; else a[x-2]++;
- }
- for (i=1; i<=15; i++) tot[a[i]]++; work(0);
- printf("%d\n",ans);
- }
- return 0;
- }
Day2:
T1:二分答案,贪心O(N)扫过去判断。总时间O(NlogL)搞定,6、7分钟打完。
T2:我觉得T2挺简单的啊,为什么考后看贴吧这么多人不会呢。。奇怪。
动规。令f[i][j][k]表示当前第i为,匹配到母串第j个,已经匹配了k段的方案数。仅当b[i]=a[j]时f[i][j][k]!=0。然后f[i][j][k]=f[i-1][j-1][k]+Σ(x=1,j-1)f[i-1][x][k-1]。然后滚动一下空间ok,前缀和一下时间ok。20分钟打完。
T3:于是我就有3h打T3了。想到了正解不敢打。。写了80分程序拿了65分滚粗(喂喂明明有75的ccf什么机器啊)。以下是我考场上想到的解法,后来证明也是对的。我想了一下还可以做到O(N+M)时间复杂度的。
首先求欧拉序列把lca转化为rmq。之所以用欧拉序列而不是倍增是因为欧拉序列短而且后面要频繁调用lca,而rmq是O(1)的。
然后首先把每一趟运输计划的路程跑出来。对于x,y,求出他们的lca(x,y)为z,那么x和y的距离就是deep[x]+deep[y]-2*deep[z]。然后按路程从大到小排序。
然后先把最大的那个运输计划的链求出来,重新标号用线段树维护区间最大值,那么最大的那一条就是我们要改造成虫洞的,初始ans=max(dis[1]-最大的边,dis[2]),然后不断加入链求并,第i条链加入后答案为ans=max(dis[i]-此时链中最大的边,dis[i+1)。由于链是不断缩小的,所以直接调用线段树上对应区间的最小值就行了,不用删除。
至于怎么求两条链的并。。我是用四个lca求的,画个图自己YY一下就好了。注意我还用了一个判断函数isson(x,y)。为真时表示y是x的祖先。由于再这道题中调用的x,y在同一条到根节点的路上所以直接用pos数组比较就行了。
好了至此就做完了时间复杂度O((N+M)logN)。怎么去掉log呢?首先这个是±1rmq可以用神奇的方法做到O(N)预处理O(1)查询,这个自己找一下好了。排序嘛。。先后5位排一下,再前面几位排一下两个基数排序搞定O(N)。线段树查询。。那就把每一次的链记录一下倒过来就可以不用线段树直接求最大值了,每一次更新一下就好了嘛。所以就O(N+M)了,当然我没蛋疼去写这个程序。
AC代码如下(当然是O((N+M)logN)的啦):
- #include<iostream>
- #include<cstdio>
- #include<cstring>
- #include<algorithm>
- #define N 600005
- #define inf 1000000000
- using namespace std;
- bool bo[N]; int n,m,cnt,tot,dfsclk,fst[N],pnt[N],len[N],nxt[N];
- int f[N][21],pwr[30],id[N],pos[N],fa[N],lgt[N],c[N][3],srl[N],num[N],d[N];
- struct node{ int x,y,z; }a[N];
- bool cmp(node aa,node bb){ return aa.z>bb.z; }
- int read(){
- int x=0; char ch=getchar();
- while (ch<'0' || ch>'9') ch=getchar();
- while (ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); }
- return x;
- }
- void add(int aa,int bb,int cc){
- pnt[++tot]=bb; len[tot]=cc; nxt[tot]=fst[aa]; fst[aa]=tot;
- }
- void dfs(int x,int last,int dep){
- id[++dfsclk]=x; d[x]=dep;
- pos[x]=f[dfsclk][0]=dfsclk; int p;
- for (p=fst[x]; p; p=nxt[p]){
- int y=pnt[p]; if (y==last) continue;
- fa[y]=x; dfs(y,x,dep+len[p]); f[++dfsclk][0]=pos[x];
- }
- }
- int lca(int aa,int bb){
- if (aa==bb) return aa;
- int x=pos[aa],y=pos[bb];
- if (x>y) swap(x,y); int k=lgt[y-x+1];
- return id[min(f[x][k],f[y-pwr[k]+1][k])];
- }
- bool isson(int x,int y){ return pos[x]>pos[y]; }
- void getlst(int x,int last){
- srl[x]=++cnt;
- if (x==a[1].y) return; int p;
- for (p=fst[x]; p; p=nxt[p]){
- int y=pnt[p]; if (y==last) continue;
- if (bo[y]){ num[cnt]=len[p]; getlst(y,x); }
- }
- }
- void build(int k,int x,int y){
- c[k][0]=x; c[k][1]=y;
- if (x==y){ c[k][2]=num[x]; return; }
- int mid=(x+y)>>1;
- build(k<<1,x,mid); build((k<<1)|1,mid+1,y);
- c[k][2]=max(c[k<<1][2],c[(k<<1)|1][2]);
- }
- int getmax(int k,int x,int y){
- int l=c[k][0],r=c[k][1],mid=(l+r)>>1;
- if (l==x && r==y) return c[k][2];
- if (y<=mid) return getmax(k<<1,x,y); else
- if (x>mid) return getmax((k<<1)|1,x,y); else
- return max(getmax(k<<1,x,mid),getmax((k<<1)|1,mid+1,y));
- }
- bool ok(int x,int p,int q){ return p!=q && (isson(p,x) || isson(q,x)); }
- int main(){
- n=read(); m=read(); int i,j;
- for (i=1; i<n; i++){
- int x=read(),y=read(),z=read();
- add(x,y,z); add(y,x,z);
- }
- dfs(1,0,0); pwr[0]=1;
- for (i=1; i<=20; i++) pwr[i]=pwr[i-1]<<1;
- lgt[1]=0; for (i=2; i<=pwr[20]; i++) lgt[i]=lgt[i>>1]+1;
- //for (i=1; i<=n; i++) cout<<lgt[i]<<' '; puts("");
- for (i=1; i<=20; i++)
- for (j=1; j<=dfsclk; j++){
- f[j][i]=f[j][i-1];
- if (j+pwr[i-1]<=dfsclk) f[j][i]=min(f[j][i],f[j+pwr[i-1]][i-1]);
- }
- for (i=1; i<=m; i++){
- a[i].x=read(); a[i].y=read();
- a[i].z=d[a[i].x]+d[a[i].y]-(d[lca(a[i].x,a[i].y)]<<1);
- }
- sort(a+1,a+m+1,cmp);
- int tmp=lca(a[1].x,a[1].y),ans=inf;
- for (i=a[1].x; i!=tmp; i=fa[i]) bo[i]=1;
- for (i=a[1].y; i!=tmp; i=fa[i]) bo[i]=1; bo[tmp]=1;
- int u=a[1].x,v=a[1].y; i=1;
- if (u==v){ puts("0"); return 0; }
- getlst(a[1].x,0); build(1,srl[u],srl[v]-1);
- while (u!=v){
- if (srl[u]>srl[v]) swap(u,v);
- i++; ans=min(ans,max(a[1].z-getmax(1,srl[u],srl[v]-1),a[i].z));
- tmp=lca(u,v); int p=a[i].x,q=a[i].y;
- int t1=lca(u,p),t2=lca(u,q),t3=lca(v,p),t4=lca(v,q);
- if (!ok(tmp,t1,t2)) u=tmp; else{
- if (isson(t1,t2)) swap(t1,t2);
- if (isson(t1,tmp)) v=t1;
- u=t2;
- }
- tmp=lca(u,v);
- if (!ok(tmp,t3,t4)) v=tmp; else{
- if (isson(t3,t4)) swap(t3,t4);
- if (isson(t3,tmp)) u=t3;
- v=t4;
- }
- }
- printf("%d\n",ans); return 0;
- }
by lych