10.8&10.10
10.8:
A:这个我们对于每个集合,建一个新的点,这给点向其他边连一个权值为0的边,所有点向这个边连一个权值为w的边;
然后直接跑最短路就好了;
#include<cstdio> #include<cstring> #include<queue> #include<cctype> #define int long long using namespace std; int n,m; int read() { int f=1,x=0; char ch=' '; for(;!isdigit(ch);ch=getchar())if(ch=='-')f*=-1; for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0'; return f*x; } struct edge { int v,last,w; }e[1000010]; int in[1000010],cnt=0; int a[1000010]; void add(int x,int y,int z) { e[++cnt].v=y; e[cnt].w=z; e[cnt].last=in[x]; in[x]=cnt; } const int inf=0x7f7f7f7f7f7f7f7f; struct node { int dist,pos; bool operator <(const node &x)const { return x.dist<dist; } }; priority_queue<node> q; int dis[1000010]; bool vis[1000010]; void dijkstra(int st) { int i,x,y; node tmp; memset(vis,0,sizeof(vis)); //for(i=1;i<=n;i++) dis[i]=inf; memset(dis,0x7f,sizeof(dis)); dis[st]=0; q.push((node){0,st}); while(!q.empty()) { tmp=q.top();q.pop(); x=tmp.pos; if(vis[x]!=0)continue; vis[x]=1; for(i=in[x];i;i=e[i].last) { y=e[i].v; if(dis[y]>dis[x]+e[i].w) { dis[y]=dis[x]+e[i].w; if(vis[y]==0) q.push((node){dis[y],y}); } } } } signed main() { //freopen("way.in","r",stdin); //freopen("way.out","w",stdout); int i,st,ed,q,now; n=read(); now=n+1; q=read(); while(q--) { int m,w; m=read(); for(i=1;i<=m;i++) { a[i]=read(); add(now,a[i],0); } w=read(); for(int i=1;i<=m;i++) add(a[i],now,w); now++; } st=read(); ed=read(); dijkstra(st); if(dis[ed]>=inf)printf("-1\n"); else printf("%lld\n",dis[ed]); return 0; }
B:维护两个直径端点的集合,每次加入一个点的时候根据能不能更新直径来清空一个集合并将其加入;
如果一个端点同时归于两个集合,那么正常维护;
在清空一个集合的时候遍历一遍这个集合,看看是否属于这类点;
#include<cstdio> #include<algorithm> #include<cctype> #include<vector> using namespace std; int read() { int f=1,x=0; char ch=' '; for(;!isdigit(ch);ch=getchar())if(ch=='-')f*=-1; for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0'; return f*x; } int n,mmax; vector<int>s1,s2; void swt(int &a,int &b){a^=b^=a^=b;} const int LG=21; int dep[300010]; int f[300010][21]; /*void dfs(int cur,int fa) { int i,t; dep[cur]=dep[fa]+1; for(i=1;i<=LG-1;i++) { f[cur][i]=f[f[cur][i-1]][i-1]; } for(i=in[cur];i;i=e[i].last) { t=e[i].v; if(t==fa)continue; f[t][0]=cur; dfs(t,cur); } return; }*/ void dfs(int u,int fa) { dep[u]=dep[fa]+1; f[u][0]=fa; for(int i=1;(1<<i)<=dep[u];i++) f[u][i]=f[f[u][i-1]][i-1]; } int lca(int x,int y) { if(x==y)return x; if(dep[x]<dep[y])swt(x,y); int l=dep[x]-dep[y],i,lst,ans=0; for(i=0;i<=LG-1;i++) if(l>>i&1)lst=x,x=f[x][i],ans+=dep[lst]-dep[x]; if(x==y)return ans; for(i=LG-1;i>=0;i--) { if(f[x][i]!=f[y][i]) lst=x,x=f[x][i],y=f[y][i],ans+=2*(dep[lst]-dep[x]); } return ans+2; } int main() { n=read();n++; dfs(1,0); s1.push_back(1); mmax=1; for(int i=2;i<=n;i++) { int t=read(); dfs(i,t); int d1=s1.empty()?0:lca(s1[0],i); int d2=s2.empty()?0:lca(s2[0],i); if(max(d1,d2)>mmax) { mmax=max(d1,d2); if(d1==mmax) { for(int j=0;j<s2.size();j++) if(lca(i,s2[j])==mmax) s1.push_back(s2[j]); s2.clear(); } else { for(int j=0;j<s1.size();j++) if(lca(i,s1[j])==mmax) s2.push_back(s1[j]); s1.clear(); } } if(max(d1,d2)==mmax) (d1==mmax?s2:s1).push_back(i); printf("%d\n",s1.size()+s2.size()); } return 0; }
C:考虑到gcd(n,m)=1那么我们可以把每个数表示成a*n+b*m的形式,就把它转化为坐标记为(a,b);
终止点3种为(2m,0),(m,n),(0,2n),1,3很好的能靠判断过;
考虑2,f[i]表示从i号点到终止节点不经过别的障碍点的方案数,那么它可以由右上方的点转移过来;
最后的答案=总方案数-每个点右上方不经过障碍点的方案数*到这个点不考虑是否经过障碍点的方案数
总结: A:题很顺利的过了
B:T2写了一个最暴力的lca,只有20
C:记搜卡时,不知道为什么挂了(应该有20的)
(文操最后因为没检查,少了一些奇怪的东西,然后。。。。。)
10.10
A:直接枚举左端点,然后用r维护另一个端点,并记录当前1的个数
#include<cstdio> #include<algorithm> #include"xor.h" #define int long long int a[16000010]; signed main() { //freopen("xor.in","r",stdin); //freopen("xor.out","w",stdout); long long n; get(a+1,n); int l=1,r=1,now=0; int ans=0; while(l<=n) { while((r<=n)&&((now+a[r])==(now^a[r]))) { now^=a[r++]; } ans+=r-l; now^=a[l]; l++; } printf("%lld\n",ans); return 0; }
//这个只有提交的时候才能过,自测因为文件名会挂
B:枚举流量,对于每个不同的流量,跑一边dij,判断取得所有边都是>=lim_flow的,然后取max
#include<cstdio> #include<cstring> #include<queue> #include<cctype> #include<algorithm> using namespace std; int read() { int f=1,x=0; char ch=' '; for(;!isdigit(ch);ch=getchar())if(ch=='-')f*=-1; for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0'; return f*x; } int n,m; struct edge { int v,last,w; int l; }e[100010]; int in[100010],cnt=0; int flow[100010]; int d[100010]; int fin[100010]; void add(int x,int y,int z,int l) { e[++cnt].v=y; e[cnt].w=z; e[cnt].last=in[x]; flow[cnt]=l; in[x]=cnt; } const int inf=1000000010; struct node { int dist,pos; bool operator <(const node &x)const { return x.dist<dist; } }; priority_queue<node> q; int dis[100010]; bool vis[100010]; void dijkstra(int les) { int x,y; int st=1; node tmp; memset(vis,0,sizeof(vis)); memset(dis,inf,sizeof(dis)); dis[st]=0; q.push((node){0,st}); while(!q.empty()) { tmp=q.top();q.pop(); x=tmp.pos; if(vis[x]!=0)continue; vis[x]=1; for(int i=in[x];i;i=e[i].last) { y=e[i].v; if(dis[y]>dis[x]+e[i].w&&flow[i]>=les) { dis[y]=dis[x]+e[i].w; if(vis[y]==0) q.push((node){dis[y],y}); } } } } bool cmp(int x,int y) { return x>y; } int main() { //freopen("water.in","r",stdin); //freopen("water.out","w",stdout); int su; n=read(),m=read(),su=read(); for(int i=1;i<=m;i++) { int u,v,val; u=read(),v=read(),val=read(),d[i]=read(); add(u,v,val,d[i]); add(v,u,val,d[i]); } sort(d+1,d+1+m,cmp); //int now=1; //fin[now]=d[1]; /*for(int i=2;i<=m;i++) { if(d[i]!=d[i-1]) fin[++now]=d[i]; }*/ int ans=1000000010; for(int i=1;i<=m;i++) { if(i==m||(su/d[i])!=(su/d[i+1])) { //printf("!"); dijkstra(d[i]); if(dis[n]<100000010) { int qwq=dis[n]+su/d[i]; ans=min(ans,qwq); } } } if(ans==1000000010) printf("-1\n"); else printf("%d\n",ans); return 0; }
C:对于每一个不同的位,假设为a,b则在1序列中判断第一个b到这位a上所有的数都>b,即可完成替换,线段树维护
#include<cstdio> #include<cctype> #include<queue> #include<algorithm> #define ls x<<1 #define rs x<<1|1 #define inf 0x3f3f3f3f #define maxn 100010 using namespace std; int read() { int f=1,x=0; char ch=' '; for(;!isdigit(ch);ch=getchar()) if(ch=='-') f*=-1; for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0'; return f*x; } int a[maxn],b[maxn],ta[maxn],tb[maxn]; int n; int t[maxn<<2]; void pushup(int x) { t[x]=min(t[ls],t[rs]); } void build(int l,int r,int x) { if(l==r) { t[x]=a[l]; return ; } int mid=(l+r)/2; build(l,mid,ls); build(mid+1,r,rs); pushup(x); } void update(int pos,int val,int l,int r,int x) { if(l==r) { t[x]=val; return ; } int mid=(l+r)/2; if(pos<=mid) update(pos,val,l,mid,ls); else update(pos,val,mid+1,r,rs); pushup(x); } int query(int LL,int RR,int l,int r,int x) { if(LL<=l&&RR>=r) return t[x]; int mid=(l+r)/2; int ans=inf; if(LL<=mid) ans=min(ans,query(LL,RR,l,mid,ls)); if(RR>mid) ans=min(ans,query(LL,RR,mid+1,r,rs)); return ans; } queue<int> q[maxn]; int main() { //freopen("exam.in","r",stdin); int t=read(); while(t--) { int n=read(); for(int i=1;i<=n;i++) { a[i]=read(); ta[i]=a[i]; while(q[a[i]].size()) q[a[i]].pop(); } for(int i=1;i<=n;i++) { b[i]=read(); tb[i]=b[i]; } sort(ta+1,ta+1+n); sort(tb+1,tb+1+n); int flag=0; for(int i=1;i<=n;i++) if(ta[i]!=tb[i]) flag=1; if(!flag) { build(1,n,1); for(int i=1;i<=n;i++) q[a[i]].push(i); for(int i=1;i<=n;i++) { int pos=q[b[i]].front(); q[b[i]].pop(); int tmp=query(1,pos,1,n,1); update(pos,inf,1,n,1); if(tmp<b[i])flag=1; } } if(flag) printf("NO\n"); else printf("Yes\n"); } return 0; }
总结:A:很快过了
B:很快过了(但因为最后少加了一条边并且没有进行优化,就炸了)
C:最后1H想到正解,但是线段树死了,就打了70分暴力(不知道为什么只剩50)