2017.8.27 考试
考场做法:
把所有的边按编号小的点从大到小排序
依次加边,并查集维护
合并时 fa[]强制为编号小的点
当出现环时,如果fa<=k,ans++
#include<cstdio> #include<iostream> #include<algorithm> #define N 1000001 #define M 2000001 using namespace std; int fa[N]; struct node { int u,v; }e[M]; void read(int &x) { x=0; char c=getchar(); while(!isdigit(c)) c=getchar(); while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); } } int find(int i) { return fa[i]==i ? fa[i] : fa[i]=find(fa[i]); } bool cmp(node p,node q) { if(p.u!=q.u) return p.u>q.u; return p.v>q.v; } int main() { freopen("tour.in","r",stdin); freopen("tour.out","w",stdout); int n,m,k; read(n); read(m); read(k); for(int i=1;i<=n;i++) fa[i]=i; int u,v,r1,r2,tmp1=0,tmp2=0,tot=0; for(int i=1;i<=m;i++) { read(u); read(v); if(u==v) { tmp1++; continue; } e[++tot].u=min(u,v); e[tot].v=max(u,v); } m=tot; sort(e+1,e+m+1,cmp); for(int i=1;i<=m;i++) { u=e[i].u; v=e[i].v; r1=find(u); r2=find(v); if(r1!=r2) { if(r1<r2) swap(r1,r2); fa[r1]=r2; } else if(fa[r1]<=k) tmp2++; } printf("%d",tmp1+tmp2); }
上面解法中的排序没有必要
将边分为两类
1、定点编号都>k
2、至少有一个点的编号<=k
先把第一类用并查集合并
合并第二类时,遇环就ans++
#include<cstdio> #include<iostream> #define N 1000001 #define M 2000001 using namespace std; int fa[N]; struct node { int u,v; }e[M]; int find(int i) { return fa[i]==i ? i : fa[i]=find(fa[i]); } int read(int &x) { x=0; char c=getchar(); while(!isdigit(c)) c=getchar(); while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); } } int main() { freopen("tour.in","r",stdin); freopen("tour.out","w",stdout); int n,m,k; read(n); read(m); read(k); for(int i=1;i<=n;i++) fa[i]=i; int u,v,r1,r2,tot=0; for(int i=1;i<=m;i++) { read(u); read(v); if(u>k && v>k) { r1=find(u); r2=find(v); fa[r1]=r2; } else { e[++tot].u=u; e[tot].v=v; } } int ans=0; for(int i=1;i<=tot;i++) { r1=find(e[i].u); r2=find(e[i].v); if(r1==r2) ans++; fa[r1]=r2; } printf("%d",ans); }
每个数最多开6次就变成1了
线段树维护区间是否全变成1
全变成1了就跳过
注意l可能大于r,被坑了
#include<cmath> #include<cstdio> #include<iostream> #define N 100001 using namespace std; typedef long long LL; LL sum[N<<2],ans; int mid[N<<2]; bool f[N<<2]; int opl,opr; void read(int &x) { x=0; char c=getchar(); while(!isdigit(c)) c=getchar(); while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); } } void read(LL &x) { x=0; char c=getchar(); while(!isdigit(c)) c=getchar(); while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); } } void build(int k,int l,int r) { if(l==r) { read(sum[k]); if(sum[k]==1) f[k]=true; return; } mid[k]=l+r>>1; build(k<<1,l,mid[k]); build(k<<1|1,mid[k]+1,r); sum[k]=sum[k<<1]+sum[k<<1|1]; f[k]=f[k<<1]&f[k<<1|1]; } void change(int k,int l,int r) { if(f[k]) return; if(l==r) { sum[k]=sqrt(sum[k]); if(sum[k]==1) f[k]=true; return; } if(opl<=mid[k]) change(k<<1,l,mid[k]); if(opr>mid[k]) change(k<<1|1,mid[k]+1,r); sum[k]=sum[k<<1]+sum[k<<1|1]; f[k]=f[k<<1]&f[k<<1|1]; } void query(int k,int l,int r) { if(l>=opl && r<=opr) { ans+=sum[k]; return; } if(opl<=mid[k]) query(k<<1,l,mid[k]); if(opr>mid[k]) query(k<<1|1,mid[k]+1,r); } void out(LL x) { if(x/10) out(x/10); putchar(x%10+'0'); } int main() { freopen("sequence.in","r",stdin); freopen("sequence.out","w",stdout); int n,m,ty; read(n); build(1,1,n); read(m); while(m--) { read(ty); read(opl); read(opr); if(opl>opr) swap(opl,opr); if(!ty) change(1,1,n); else ans=0,query(1,1,n),out(ans),printf("\n"); } }
单词i向单词j连一条边,边权为j接到i后面所需的增加的长度
0向每个单词连边权为单词长度的边
那么题意转化为在这张图上走m步的最短长度
单词只有200,出现次数很大
用倍增加速floyd
#include<iostream> #include<cstring> #include<cstdio> #include<vector> #define N 201 #define M 100001 using namespace std; typedef unsigned long long LL; const int seed=131; int n,m; string s[N]; vector<LL>hash[N]; LL bit[M],h[M]; struct mat { LL a[N][N]; void init() { memset(a,-1,sizeof(a)); } mat operator * (mat b) { mat res; res.init(); for(int k=0;k<=n;k++) for(int i=0;i<=n;i++) if(a[i][k]!=-1) for(int j=0;j<=n;j++) if(b.a[k][j]!=-1) res.a[i][j]=res.a[i][j]<0 ? a[i][k]+b.a[k][j] : min(res.a[i][j],a[i][k]+b.a[k][j]); return res; } }; void out(LL x) { if(x/10) out(x/10); putchar(x%10+'0'); } void solve() { mat x; x.init(); for(int i=1;i<=n;i++) x.a[0][i]=s[i].length(); for(int i=1;i<=n;i++) { int t1=s[i].length(),mx=0; LL cur=0; for(t1--;t1>=0;--t1) { cur+=s[i][t1]*bit[mx]; h[mx++]=cur; } for(int j=1;j<=n;j++) { int len=min(s[i].length(),s[j].length()),res=0; if(i==j) --len; for(int k=0;k<len;k++) if(h[k]==hash[j][k]) res=k+1; x.a[i][j]=s[j].length()-res; } } mat res; res=x; for(--m;m;m>>=1,x=x*x) if(m&1) res=res*x; LL ans=1e18; for(int i=1;i<=n;i++) ans=min(ans,res.a[0][i]); out(ans); } int main() { freopen("hamster.in","r",stdin); freopen("hamster.out","w",stdout); scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { cin>>s[i]; LL cur=0; int len=s[i].length(); for(int j=0;j<len;j++) { cur=cur*seed+s[i][j]; hash[i].push_back(cur); } } bit[0]=1; for(int i=1;i<M;i++) bit[i]=bit[i-1]*seed; solve(); }
考场kmp预处理+dfs10分代码:
#include<ctime> #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> using namespace std; char s[201][100001]; char ss[200001]; int tim,n,m,minn=1e5+1,cut[201][201],len[201],f[201],ans; int kmp(int a,int l) { int j; for(int i=1;i<l;i++) { j=f[i]; while(j && ss[i]!=ss[j]) j=f[j]; f[i+1]= ss[i]==ss[j] ? j+1 : 0; } int now=f[l]; while(now>=a) now=f[now]; return now; } void dfs(int now,int last,int length,bool have) { if(clock()-tim>=2980) { printf("%d",ans); exit(0); } if(length>=ans) return; if(now==m+1) { ans=length; return; } for(int i=1;i<=n;i++) if(!last || cut[i][last] || have) dfs(now+1,i,length+len[i]-cut[i][last],cut[i][last]); } int main() { freopen("hamster.in","r",stdin); freopen("hamster.out","w",stdout); tim=clock(); scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { scanf("%s",s[i]); len[i]=strlen(s[i]); minn=min(minn,len[i]); } int l; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { l=0; for(int k=0;k<len[i];k++) ss[l++]=s[i][k]; for(int k=0;k<len[j];k++) ss[l++]=s[j][k]; cut[i][j]=kmp(len[i],l); } ans=minn*m; dfs(1,0,0,false); printf("%d",ans); }