T1
“开心题”
每个点如果不是根节点,子树个数等于度数减一。然后随便算一算
T2
数据范围
首先对于两个长度相等的偶数串。把他两两分组
1 | 2 | 3 | 4 |
5 | 6 | 7 | 8 |
考虑一个和8相等的(包括交换内部后)的块
先把他调到头部然后调到尾部。然后删除这一块
然后发现这样的可以对每个串求出字典序最小的等价表达。。其实随便计算一下就解决了
#include<cstdio> #include<string.h> #include<iostream> #include<algorithm> using namespace std; int n,sc[55],len[55],ls[55][66],_ls[55]; char mz[55][66]; bool same(pair<char,char>p[],int b){ pair<char,char >pb[55];int _p=0; for(int j=1;j<=len[b]/2;j++){ if(mz[b][(j<<1)-1]>mz[b][j<<1]) pb[++_p]=make_pair(mz[b][(j<<1)],mz[b][(j<<1)-1]); else pb[++_p]=make_pair(mz[b][(j<<1)-1],mz[b][(j<<1)]); } sort(pb+1,pb+_p+1); for(int i=1;i<=_p;i++){ if(p[i]!=pb[i]){ return false; } }return true; } int main(){ freopen("list.in","r",stdin); freopen("list.out","w",stdout); scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%s",mz[i]+1);len[i]=strlen(mz[i]+1); } for(int i=1;i<=n;i++){ if(!sc[i]){ for(int j=1;j<=len[i]/2;j++){ if(mz[i][(j<<1)-1]>mz[i][j<<1]) p[++_p]=make_pair(mz[i][(j<<1)],mz[i][(j<<1)-1]); else p[++_p]=make_pair(mz[i][(j<<1)-1],mz[i][(j<<1)]); } sort(p+1,p+_p+1); for(int j=1;j<=n;j++){ if(len[i]!=len[j]||(len[i]&1)!=(len[j]&1))continue; if((len[i]&1)&&(len[j]&1)){ if(mz[i][len[i]]!=mz[i][len[j]])continue; } if(same(p,j)){ sc[i]=sc[j]=true; ls[i][++_ls[i]]=j; } } } } int ans=0; for(int i=1;i<=n;i++){ ans+=(_ls[i]&1); } cout<<ans; return 0; }
T3
对于一个图。给定固定点对,求删边一条边后点对的最短路
20%暴力
//segment tree beats! //I can program a segment but tree,not for this problem #include<queue> #include<cstdio> #include<iostream> #include<algorithm> using namespace std; typedef long long ll; ll inf =1000000000ll*1000000000ll; struct edge{ int to,next,v; }e[400005]; int n,m,q,_s,_t,du,dv,last[210000],cnt,inq[200001]; ll dis[200001]; void insert_directed(int a,int b,int c){e[++cnt]=(edge){b,last[a],c};last[a]=cnt;} void link(int a,int b,int c){insert_directed(a,b,c);insert_directed(b,a,c);} int gg; ll spfa(int s,int t){ queue<int>q; for(int i=1;i<=n;i++)dis[i]=1000000000ll*1000000000ll,inq[i]=false; q.push(s);dis[s]=0;inq[s]=true; while(!q.empty()){ int c=q.front();q.pop();inq[c]=false; for(int i=last[c];i;i=e[i].next){ int x=e[i].to; if(c==du&&x==dv)continue; if(c==dv&&x==du)continue; if(dis[c]+e[i].v<dis[x]){ dis[x]=dis[c]+e[i].v; if(!inq[x]){ q.push(x);inq[x]=true; } } } } return dis[t]; } namespace luangao{ int pre[200001]; ll spfa(int s,int t){ queue<int>q; for(int i=1;i<=n;i++)dis[i]=1000000000ll*1000000000ll,inq[i]=false; q.push(s);dis[s]=0;inq[s]=true; while(!q.empty()){ int c=q.front();q.pop();inq[c]=false; for(int i=last[c];i;i=e[i].next){ int x=e[i].to; if(c==du&&x==dv)continue; if(c==dv&&x==du)continue; if(dis[c]+e[i].v<dis[x]){ pre[x]=c; dis[x]=dis[c]+e[i].v; if(!inq[x]){ q.push(x);inq[x]=true; } } } } return dis[t]; } ll hs(int a,int b){ return a*200001+b; } ll H[200001];int _H=0; bool find(ll x){ int l=1,r=_H,mid; while(l<=r){ //if(x==hs(1,2))cout<<l <<' '<<r<<endl; mid=(l+r)/2; if(x==H[mid])return true; if(x>H[mid])l=mid+1; else r=mid-1; } return x==H[mid]; } int main(){ ll sssp=spfa(_s,_t); int t=_t; while(pre[t]){ H[++_H]=hs(pre[t],t); H[++_H]=hs(t,pre[t]); t=pre[t]; } sort(H+1,H+_H+1); //for(int i=1;i<=_H;i++)cout<<H[i]<<endl; //cout<<hs(1,2)<<endl; for(int i=1;i<=q;i++){ scanf("%d %d",&du,&dv); ll x=hs(du,dv); //cout<<find(x)<<':'; ll ans; if(find(x))ans=spfa(_s,_t); else ans=sssp; if(ans>=inf)puts("Infinity"); else printf("%I64d ",ans); } return 0; } } int main(){ freopen("dream.in","r",stdin); freopen("dream.out","w",stdout); scanf("%d %d",&n,&m); for(int i=1;i<=m;i++){ int u,v,va; scanf("%d %d %d",&u,&v,&va);link(u,v,va); } scanf("%d %d %d" ,&_s,&_t,&q); if(n<=4000&&m<=4000) for(int i=1;i<=q;i++){ scanf("%d %d",&du,&dv); ll ans=spfa(_s,_t); if(ans>=inf)puts("Infinity"); else printf("%I64d ",ans); } else luangao::main(); }
正解
两种做法
一种线段树的。http://tonyfang.is-programmer.com/posts/205232.html
OrzFang%%%
第二种堆的做法==
考虑一个最短路图的情况。最短路图是每一条边都在最短路上的边
那么影响答案的肯定是割边被删除
那么我们考虑s-t中的联通块依序处理。如果是这之间的割边答案会变成这个和后面所有的联通块之间连边最小值与两端到s、t的最短路和
扫描一下联通块
这样每条边都会进堆出堆一次 复杂度得到保证