湖南省队集训题【2018】(我也不知道是第几场)
T1 :
同 BZOJ 3658
可以扫描线(非要写可持久化数据结构也不拦着你)。
我们先处理在直线下方的东西。
先把所有点都按x轴排序。相同颜色的点用链表连起来。那么答案就应该在相邻的两个同色点之间的全部点或同色中最前或最后的点和边界之间的所有点。这个我们可以统计出来。
我们按照y轴排序,一排一排的删(是先把同样y的所有点删去后再统计答案),
我们考虑如何统计答案,我们在链表中删除这个点后询问 相邻两个同一颜色的点之间有多少点。而这个东西我们可以用树状数组维护。
直线上方同理。
#pragma GCC optimize("-O2") #include<bits/stdc++.h> #define N 600007 #define L(x) (x&-x) #define LL long long #define int LL using namespace std; struct Node { LL x,y;int col; }p[N]; #define sight(x) ('0'<=x&&x<='9') template <class T> inline void read(T &x){ static char c;static int b; for (b=1,c=getchar();!sight(c);c=getchar())if (c=='-') b=-1; for (x=0;sight(c);c=getchar())x=x*10+c-48; x*=b; } int l[N],r[N],head[N],pre[N],net[N],n,ans,pp[N],top,k; bool cmp(Node X,Node Y) { return X.x<Y.x; } bool cmp2(int x,int y) { return p[x].y<p[y].y; } void write(int x){if (x<10) {putchar('0'+x); return;} write(x/10); putchar('0'+x%10);} inline void writeln(int x){ if (x<0) putchar('-'),x*=-1; write(x); putchar('\n'); } inline void writel(int x){ if (x<0) putchar('-'),x*=-1; write(x); putchar(' '); } struct Tree{ int c[N]; void clear() {memset(c,0,sizeof c);} void add(int x,int dla) { for (;x<N;x+=L(x)) c[x]+=dla; } int que(int x) { static int anw; for (anw=0;x;x-=L(x)) anw+=c[x]; return anw; } }T; void link() { memset(head,0,sizeof head); memset(pre,0,sizeof pre); memset(net,0,sizeof net); T.clear(); for (int i=1;i<=n;i++) T.add(i,1); for (int i=1;i<=n;i++) { net[i]=head[p[i].col]; pre[head[p[i].col]]=i; head[p[i].col]=i; } } int x,tt,tot; void sol() { ans=0; memset(l,0,sizeof l); memset(r,0,sizeof r); read(n); read(k); for (int i=1;i<=n;i++) pp[i]=i; for (int i=1;i<=n;i++) read(p[i].x),read(p[i].y),read(p[i].col); sort(p+1,p+n+1,cmp); l[1]=1; r[n]=n; for (int i=2;i<=n;i++) if (p[i].x==p[i-1].x) l[i]=l[i-1]; else l[i]=i; for (int i=n-1;i;i--) if (p[i].x==p[i+1].x) r[i]=r[i+1]; else r[i]=i; link(); sort(pp+1,pp+n+1,cmp2); for (int i=1;i<=k;i++) { if (head[i]==0) {writeln(n);return;} top=head[i]; ans=max(ans,n-r[top]); while (top) { ans=max(ans,l[top]-r[net[top]]-1); top=net[top]; } } tot=1; for (int i=1;i<=n;i++) { while (p[pp[tot]].y==p[pp[i]].y) T.add(pp[tot],-1),tot++; x=pp[i]; if (pre[x]) ans=max(ans,T.que(l[pre[x]]-1)-T.que(r[net[x]])); else ans=max(ans,T.que(n)-T.que(r[net[x]])); pre[net[x]]=pre[x]; net[pre[x]]=net[x]; } link(); tot=n; for (int i=n;i;i--) { while (p[pp[tot]].y==p[pp[i]].y&&tot) T.add(pp[tot],-1),tot--; x=pp[i]; if (pre[x]) ans=max(ans,T.que(l[pre[x]]-1)-T.que(r[net[x]])); else ans=max(ans,T.que(n)-T.que(r[net[x]])); pre[net[x]]=pre[x]; net[pre[x]]=net[x]; } writeln(ans); } signed main () { freopen("j.in","r",stdin); freopen("j.out","w",stdout); read(tt); while (tt--) sol(); }
T2:
同 BZOJ 3659
应该是有个定理的,but有要求,要每一个点的入度等于出度。但是标程没判。然后我就爆0了。(⊙o⊙)…
思考欧拉回路的性质,每一条边都要经过,而且要成回路,显然入度要等于出度。否则答案为0.
#include<bits/stdc++.h> using namespace std; #define LL long long #define N 507 #define M 200007 #define sight(x) ('0'<=x&&x<='9') #define P 1000003 LL fac[M],anw;int d[N],t; inline void read(int &x){ static char c; for (c=getchar();!sight(c);c=getchar()); for (x=0;sight(c);c=getchar())x=x*10+c-48; } vector<int> v[M]; void write(LL x){if (x<10) {putchar('0'+x); return;} write(x/10); putchar('0'+x%10);} inline void writeln(LL x){ if (x<0) putchar('-'),x*=-1; write(x); putchar('\n'); } inline void writel(LL x){ if (x<0) putchar('-'),x*=-1; write(x); putchar(' '); } inline LL qsm(LL x,LL y=P-2) { static LL anw; for (anw=1;y;y>>=1,x=x*x%P) if (y&1) anw=anw*x%P; return anw; } int to[M],tim,x,dd[N]; struct Graph{ int n,m; LL a[N][N]; LL val; void clear() { memset(a,0,sizeof a); } bool add(int x,int y) { if (x==y) return 0; if (x==0||y==0) return 1; x--; y--; a[x][x]++; a[x][y]--; return 0; } LL calc() { n--; if (n==0) return 1; LL ans=1; for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) a[i][j]=(a[i][j]%P+P)%P; for (int i=1;i<=n;i++) { for (int j=i+1;j<=n;j++) { LL A=a[i][i],B=a[j][i]; while (B) { LL t=A/B;A%=B;swap(A,B); for (int k=i;k<=n;k++) a[i][k]=(a[i][k]-t*a[j][k]%P+P)%P; for (int k=i;k<=n;k++) swap(a[i][k],a[j][k]); ans=-ans; } } ans=ans*a[i][i]%P; } return (ans+P)%P; } }G; void sol() { G.clear();memset(dd,0,sizeof dd); tim=0; memset(to,0,sizeof to); for (int i=1;i<=G.n;i++) { read(d[i]); if (d[i]) to[i]=++tim; for (int j=1;j<=d[i];j++) { read(x),v[i].push_back(x); dd[x]++; } } for (int i=1;i<=G.n;i++) { // if (dd[x]!=d[i]) {puts("0");cerr<<"hehe\n"; return; } for (int j=0;j<v[i].size();j++) if ( G.add(to[i],to[v[i][j]])) {puts("0"); return; } v[i].clear(); } anw=(G.calc()); cerr<<anw<<endl; if (G.n==1&&!d[1]) {puts("1"); return;} for (int i=1;i<=(G.n+1);i++) anw=anw*fac[d[i]-1]%P; anw=anw*d[1]%P; writeln(anw); } signed main() { freopen("w.in","r",stdin); freopen("w.out","w",stdout); fac[0]=1; for (int i=1;i<M;i++) fac[i]=fac[i-1]*i%P; while (read(G.n),G.n) sol(); return 0; }
T3:
划归于K短路。
那么有A*的做法。然而拿到的分没比暴力多。(⊙o⊙)… 考完才发现要用数据结构优化状态拓展。
#pragma GCC optimize("-O2") #include<bits/stdc++.h> #define LL long long #define N 30007 #define rr NULL LL vis[N]; #define sight(x) ('0'<=x&&x<='9') inline void read(int &x){ static char c;static int b; for (b=1,c=getchar();!sight(c);c=getchar()) if (c=='-') b=-1; for (x=0;sight(c);c=getchar())x=x*10+c-48; x=x*b; } void write(int x){if (x<10) {putchar('0'+x); return;} write(x/10); putchar('0'+x%10);} inline void writeln(int x){ if (x<0) putchar('-'),x*=-1; write(x); putchar('\n'); } inline void writel(int x){ if (x<0) putchar('-'),x*=-1; write(x); putchar(' '); } using namespace std; struct Node{ int id; LL now,will; Node() {} Node(int _id,LL _now,LL _will):id(_id),now(_now),will(_will) {} inline bool operator <(const Node &A)const{ return will>A.will; } }A; inline int rod(){ static int x=23333; return x^=x<<13,x^=x>>17,x^=x<<5; } priority_queue<Node> Q; int n,p,x[N],a[N],b[N],k,ans; LL tnow,anp; struct T{ int val,siz; LL no,nn; LL key; T* son[2]; T(LL x,LL y){ siz=1; key=x; val=rod(); son[0]=son[1]=rr; nn=no=y; } inline void rub() { siz=1; if (son[0]) siz+=son[0]->siz; if (son[1]) siz+=son[1]->siz; nn=no; if (son[0]) nn=min(nn,son[0]->nn); if (son[1]) nn=min(nn,son[1]->nn); } }*rt,*X,*y; void spilt(T* now,int k,T* &x,T* &y){ if (!now) {x=y=rr; return;} if (k>=now->key) y=now,spilt(y->son[0],k,x,y->son[0]); else x=now,spilt(x->son[1],k,x->son[1],y); now->rub(); } T* merge(T* x,T* y) { if (!x) return y; if (!y) return x; if (x->val<y->val) {x->son[1]=merge(x->son[1],y);x->rub();return x;} y->son[0]=merge(x,y->son[0]);y->rub(); return y; } void spfa(){ vis[n]=0; anp=0; rt=new T(x[n],0); for (int i=n-1;i;i--) { p=1; vis[i]=INT_MAX; spilt(rt,x[i],X,y); if (X) vis[i]=min(vis[i],b[i]+X->nn); if (y) vis[i]=min(vis[i],a[i]+y->nn); rt=merge(X,merge(new T(x[i],vis[i]),y)); } // assert(vis[1]==-5); // for (int i=n-1;i;i--) { // LL jj=INT_MAX; // for (int j=i+1;j<=n;j++) // jj=min(jj,(x[i]<=x[j]?a[i]:b[i])+vis[j]); // assert(jj==vis[i]); // } } void Astar() { Q.push(Node(1,0,vis[1])); while (k) { A=Q.top(); Q.pop(); if (A.id==n) {k--; ans=A.now; // cerr<<ans<<endl; } for (int i=A.id+1;i<=n;i++) tnow=(A.now+(x[A.id]>=x[i]?a[A.id]:b[A.id])), Q.push(Node(i,tnow,tnow+vis[i])); } } signed main () { freopen("j2.in","r",stdin); freopen("j2.out","w",stdout); read(n); read(k); for (int i=1;i<=n;i++) read(x[i]); for (int i=1;i<=n;i++) read(a[i]); for (int i=1;i<=n;i++) read(b[i]); spfa(); Astar(); writeln(ans); }
口胡一下怎么做。先用treap建出最短路树。
然后正常的套路显然是对每一点建可持久化数据结构查询K小值。里面放这个点到根路径上的新边权从小到大排的序列。
每次拓展一个节点有两种方案,撤回最近的一条边换一条更大的上去或新加一条边。
有 w1(u,v)=f[u]+w(u,v) -f[v]
但这道题边是 N^2 级别的,我们要优化这个过程。
我们 用堆优化每个点的k大点即可。