NEERC-2017
A. Archery Tournament
用线段树套set维护横坐标区间内的所有圆,查询时在$O(\log n)$个set中二分查找即可。
时间复杂度$O(n\log^2n)$。
#include<stdio.h> #include<iostream> #include<string.h> #include<string> #include<ctype.h> #include<math.h> #include<set> #include<map> #include<vector> #include<queue> #include<bitset> #include<algorithm> #include<time.h> using namespace std; void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); } #define MS(x, y) memset(x, y, sizeof(x)) #define ls o<<1 #define rs o<<1|1 #define rt 1,1,g #define mid (l+r>>1) #define lson ls,l,mid #define rson rs,mid+1,r typedef long long LL; typedef unsigned long long UL; typedef unsigned int UI; template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; } template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; } const int N = 2e6 + 10, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f; template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; } int casenum, casei; int n; struct AAA { int y, x; }a[N]; LL K(LL x) { return x * x; } bool in_circle(int o1, int o2) { int y1 = a[o1].y; int x1 = a[o1].x; int y2 = a[o2].y; int x2 = a[o2].x; return K(y1 - y2) + K(x1 - x2) < K(y2); } struct A { int ask; int id; bool operator < (const A & b)const { if(ask && b.ask) { if(a[id].y != a[b.id].y)return a[id].y < a[b.id].y; else return id < b.id; } else if(!ask && !b.ask) { if(a[id].y != a[b.id].y)return a[id].y < a[b.id].y; else return id < b.id; } else if(ask && !b.ask) { if(in_circle(id, b.id))return 1; else { if(a[id].y != a[b.id].y)return a[id].y < a[b.id].y; else return id < b.id; } } else //if(!ask && b.ask) { if(in_circle(b.id, id))return 0; else { if(a[id].y != a[b.id].y)return a[id].y < a[b.id].y; else return id < b.id; } } } }; struct Q { int tp, y, x; }q[N]; int v[N * 3]; int g; set<A>sot[N * 4]; void build(int o, int l, int r) { sot[o].clear(); if(l == r) { return; } build(lson); build(rson); } int O, L, R; void ins(int o, int l, int r) { if(L <= l && r <= R) { sot[o].insert({0, O}); return; } if(L <= mid)ins(lson); if(R > mid)ins(rson); } void del(int o, int l, int r) { if(L <= l && r <= R) { sot[o].erase({0, O}); return; } if(L <= mid)del(lson); if(R > mid)del(rson); } int P; int find(int o, int l, int r) { auto it = sot[o].lower_bound({1, 0}); if(it != sot[o].end()) { int aim = it->id; if(in_circle(0, aim))return aim; } if(l == r)return 0; if(P <= mid)return find(lson); else return find(rson); } int main() { while(~scanf("%d", &n)) { g = 0; for(int i = 1; i <= n; ++i) { scanf("%d%d%d", &q[i].tp, &q[i].x, &q[i].y); if(q[i].tp == 1) { q[i].tp = i; a[i].y = q[i].y; a[i].x = q[i].x; v[++g] = q[i].x - q[i].y; v[++g] = q[i].x + q[i].y; } else { q[i].tp = 0; v[++g] = q[i].x; } } sort(v + 1, v + g + 1); g = unique(v + 1, v + g + 1) - v - 1; build(rt); for(int i = 1; i <= n; ++i) { if(q[i].tp) { O = q[i].tp; L = lower_bound(v + 1, v + g + 1, q[i].x - q[i].y) - v + 1; R = lower_bound(v + 1, v + g + 1, q[i].x + q[i].y) - v - 1; if(L <= R) { ins(rt); } } else { P = lower_bound(v + 1, v + g + 1, q[i].x) - v; a[0].y = q[i].y; a[0].x = q[i].x; O = find(rt); if(O) { printf("%d\n", O); L = lower_bound(v + 1, v + g + 1, q[O].x - q[O].y) - v + 1; R = lower_bound(v + 1, v + g + 1, q[O].x + q[O].y) - v - 1; del(rt); } else puts("-1"); } } } return 0; } /* 【trick&&吐槽】 4 1 0 12 1 24 10 1 12 3 2 16 14 8 1 0 12 2 -11 22 1 24 10 1 12 3 2 12 12 2 16 14 1 28 15 2 3 6 【题意】 【分析】 【时间复杂度&&优化】 */
B. Box
分类讨论。
#include<stdio.h> #include<iostream> #include<string.h> #include<string> #include<ctype.h> #include<math.h> #include<set> #include<map> #include<vector> #include<queue> #include<bitset> #include<algorithm> #include<time.h> using namespace std; void fre() { } #define MS(x, y) memset(x, y, sizeof(x)) #define ls o<<1 #define rs o<<1|1 typedef long long LL; typedef unsigned long long UL; typedef unsigned int UI; template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; } template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; } const int N = 0, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f; template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; } int casenum, casei; int a[3]; int w, h; int ww, hh; bool check4() { return ww <= w && hh <= h; } bool check3() { //1 ww = a[0] * 0 + a[1] * 2 + a[2] * 2; hh = a[0] * 1 + a[1] * 0 + a[2] * 2; if(check4())return 1; //2 ww = a[0] * 0 + a[1] * 2 + a[2] * 2; hh = a[0] * 1 + a[1] * 1 + a[2] * 1; if(check4())return 1; //3 ww = a[0] * 1 + a[1] * 1 + a[2] * 3; hh = a[0] * 1 + a[1] * 1 + a[2] * 0; if(check4())return 1; //4 ww = a[0] * 1 + a[1] * 1 + a[2] * 2; hh = a[0] * 1 + a[1] * 1 + a[2] * 1; if(check4())return 1; //5 ww = a[0] * 1 + a[1] * 2 + a[2] * 1; hh = a[0] * 1 + a[1] * 0 + a[2] * 2; if(check4())return 1; //6 ww = a[0] * 2 + a[1] * 1 + a[2] * 1; hh = a[0] * 1 + a[1] * 1 + a[2] * 1; if(check4())return 1; //7 ww = a[0] * 2 + a[1] * 1 + a[2] * 0; hh = a[0] * 1 + a[1] * 1 + a[2] * 2; if(check4())return 1; return 0; } bool check2() { sort(a, a + 3); do { if(check3())return 1; }while(next_permutation(a, a + 3)); return 0; } bool check() { if(check2())return 1; swap(w, h); if(check2())return 1; else return 0; } int main() { while(~scanf("%d%d%d", &a[0], &a[1], &a[2])) { scanf("%d%d", &w, &h); puts(check() ? "Yes" : "No"); } return 0; } /* 【trick&&吐槽】 【题意】 【分析】 【时间复杂度&&优化】 */
C. Connections
考虑强连通分量的Kosaraju算法,会发现只有$2(n-1)$条边是有用的,符合题目中保留$2n$条边要求。
#include<cstdio> const int N=300010; int Case,n,m,i,x,y,cnt,use[N],g[2][N],nxt[2][N],v[2][N],ed,q[N],t,vis[N],e[N][2]; inline void add(int x,int y){ v[0][++ed]=y;nxt[0][ed]=g[0][x];g[0][x]=ed; v[1][ed]=x;nxt[1][ed]=g[1][y];g[1][y]=ed; } void dfs1(int x){ vis[x]=1; for(int i=g[0][x];i;i=nxt[0][i])if(!vis[v[0][i]])use[i]=1,dfs1(v[0][i]); q[++t]=x; } void dfs2(int x){ vis[x]=0; for(int i=g[1][x];i;i=nxt[1][i])if(vis[v[1][i]])use[i]=1,dfs2(v[1][i]); } int main(){ scanf("%d",&Case); while(Case--){ scanf("%d%d",&n,&m); for(ed=0,i=1;i<=n;i++)vis[i]=g[0][i]=g[1][i]=0; for(i=1;i<=m;i++){ scanf("%d%d",&x,&y); e[i][0]=x,e[i][1]=y; add(x,y); use[i]=0; } for(t=0,i=1;i<=n;i++)if(!vis[i])dfs1(i); for(i=n;i;i--)if(vis[q[i]])dfs2(q[i]); cnt=n*2; for(i=1;i<=m;i++)if(use[i])cnt--; for(i=1;i<=m;i++)if(!use[i]&&cnt>0)use[i]=1,cnt--; for(i=1;i<=m;i++)if(!use[i])printf("%d %d\n",e[i][0],e[i][1]); } } /* 111 4 9 1 2 1 3 2 3 2 4 3 2 3 4 4 1 4 2 4 3 */
D. Designing the Toy
斜着构造然后再补充剩下部分即可。
#include<stdio.h> #include<iostream> #include<string.h> #include<string> #include<ctype.h> #include<math.h> #include<set> #include<map> #include<vector> #include<queue> #include<bitset> #include<algorithm> #include<time.h> using namespace std; void fre() { } #define MS(x, y) memset(x, y, sizeof(x)) #define ls o<<1 #define rs o<<1|1 typedef long long LL; typedef unsigned long long UL; typedef unsigned int UI; template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; } template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; } const int N = 105, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f; template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; } int casenum, casei; int A, B, C; int a[3]; bool v[N][N]; struct AAA { AAA(int x, int y, int z) { v[0] = x; v[1] = y; v[2] = z; } int v[3]; }; vector<AAA>vt; void SWAP(int x, int y) { int g = vt.size(); for(int i = 0; i < g; ++i) { swap(vt[i].v[x], vt[i].v[y]); } } int main() { while(~scanf("%d%d%d", &A, &B, &C)) { a[0] = A; a[1] = B; a[2] = C; sort(a, a + 3); if(a[0] * a[1] < a[2]) { puts("-1"); continue; } else { vt.clear(); MS(v, 0); int cnt = a[2] - a[1]; for(int i = 1; i <= a[0]; ++i) { //vt.push_back({i, i, 1}); vt.push_back(AAA(i, i, 1)); v[i][i] = 1; } for(int i = a[0] + 1; i <= a[1]; ++i) { //vt.push_back({1, i, 1}); vt.push_back(AAA(1, i, 1)); v[1][i] = 1; } for(int i = 1; i <= a[0]; ++i) { for(int j = 1; j <= a[1]; ++j) { if(v[i][j] == 0 && cnt) { --cnt; //vt.push_back({i, j, 1}); vt.push_back(AAA(i, j, 1)); v[i][j] = 1; } } } } if(A >= max(B, C)) { if(C >= B); else SWAP(0, 1); } else if(C >= max(A, B)) { SWAP(0, 2); if(A >= B); else SWAP(1, 2); } else { SWAP(1, 2); if(C >= A); else SWAP(0, 2); } printf("%d\n", vt.size()); for(auto it : vt) { printf("%d %d %d\n", it.v[0], it.v[1], it.v[2]); } } return 0; } /* 【trick&&吐槽】 【题意】 【分析】 【时间复杂度&&优化】 */
E. Easy Quest
依次考虑每个事件,维护每种数字出现次数和待定的数字个数,当数字不够时确定一个数字。
#include<cstdio> const int N=100000; int n,i,x,c[N],cnt,m,q[N]; int main(){ scanf("%d",&n); for(i=1;i<=n;i++){ scanf("%d",&x); if(x>0)c[x]++; else if(x==0){ cnt++; }else{ if(c[-x])c[-x]--; else if(cnt){ q[++m]=-x; cnt--; }else return puts("No"),0; } } puts("Yes"); for(i=1;i<=m;i++)printf("%d ",q[i]); while(cnt--)printf("1 "); }
F. The Final Level
贪心选离目的地较近的那一维坐标靠近即可。
G. The Great Wall
二分答案,转化为统计有多少方案的权值和不超过$mid$。
整理出式子后可以通过排序后双指针+树状数组维护。
时间复杂度$O(n\log^2n)$。
#include<cstdio> #include<algorithm> using namespace std; typedef long long ll; const int N=30010; int n,len,i; int m; ll K,a[N],b[N],c[N],L,R,ANS,MID; ll f[N],g[N]; int q[N],p[N],bit[N]; inline bool cmp1(int x,int y){return f[x]<f[y];} inline bool cmp2(int x,int y){return g[x]<g[y];} inline void add(int x){for(;x<=n;x+=x&-x)bit[x]++;} inline int ask(int x){int t=0;for(;x;x-=x&-x)t+=bit[x];return t;} inline int sum(int l,int r){ l=max(l,1); r=min(r,n); if(l>r)return 0; return ask(r)-ask(l-1); } ll cal(){ int i,j; m=n-len+1; for(i=1;i<=m;i++){ f[i]=a[i-1]+b[i+len-1]-b[i-1]-a[i+len-1]; q[i]=i; } sort(q+1,q+m+1,cmp1); ll ret=0; for(i=1;i<=n;i++)bit[i]=0; for(i=m,j=1;i;i--){ while(j<=m&&f[q[i]]+f[q[j]]<=MID)add(q[j++]); ret+=sum(q[i]+len,m); } for(i=1;i<=m;i++){ f[i]=a[i-1]-b[i-1]+c[i+len-1]-b[i+len-1]; g[i]=b[i-1]-c[i-1]+b[i+len-1]-a[i+len-1]; q[i]=p[i]=i; } sort(q+1,q+m+1,cmp1); sort(p+1,p+m+1,cmp2); for(i=1;i<=n;i++)bit[i]=0; for(i=m,j=1;i;i--){ while(j<=m&&f[q[i]]+g[p[j]]<=MID)add(p[j++]); ret+=sum(q[i]+1,q[i]+len-1); } return ret; } int main(){ scanf("%d%d%lld",&n,&len,&K); for(i=1;i<=n;i++)scanf("%lld",&a[i]),a[i]+=a[i-1]; for(i=1;i<=n;i++)scanf("%lld",&b[i]),b[i]+=b[i-1]; for(i=1;i<=n;i++)scanf("%lld",&c[i]),c[i]+=c[i-1]; L=0,R=c[n]; while(L<=R){ MID=(L+R)>>1; ll tmp=cal(); if(tmp>=K)R=(ANS=MID)-1;else L=MID+1; } printf("%lld",ANS+a[n]); } /* 4 2 3 1 2 3 4 3 3 5 5 7 7 7 7 */
H. Hack
留坑。
I. Interactive Sort
考虑将偶数划分成若干不可区分大小的集合,将这些集合从小到大排序。一开始所有偶数形成一个不可区分大小的集合。
依次考虑每个奇数,将其在这些集合中二分查找,从对应集合中任取一个数进行比较,得到它大约落在哪两个集合中,再暴力遍历两个集合中所有数字,将对应集合划分成两半。同时,这个过程可以得出有多少偶数小于当前奇数,故可以确定出该奇数。
那么考虑完所有奇数后,所有偶数也就排好了序,因为数据随机,因此可以认为每次都是均分的,比较次数$O(n\log n)$。
#include<cstdio> #include<vector> using namespace std; const int N=11111,K=2; int n,m,i,j,k; int cnt; int cp; vector<int>a[N]; int fin[N]; int q[N]; //+ - vector<int>ans; inline bool cmp(int x,int y){//x<y? if(x>0){ printf("? %d %d\n",x,-y); fflush(stdout); char op[9]; scanf("%s",op); return op[0]=='<'; }else{ printf("? %d %d\n",y,-x); fflush(stdout); char op[9]; scanf("%s",op); return op[0]=='>'; } } int flag,c0,c1; int result[N]; int num[N]; inline void gao(int x,int o){ c0=c1=0; int cur=a[q[x]].size(); for(int i=0;i<cur;i++){ int y=a[q[x]][i]; num[i]=y; result[i]=cmp(y,o); if(result[i]){ c0++; }else{ c1++; } } if(!c1){ flag=1; return; } if(!c0){ flag=-1; return; } flag=0; cp++; for(int i=cp;i>x+1;i--)q[i]=q[i-1]; q[x+1]=++cnt; a[cnt].clear(); a[q[x]].clear(); for(int i=0;i<cur;i++)if(result[i])a[q[x]].push_back(num[i]); else a[cnt].push_back(num[i]); } inline void gao2(int x,int o){ int t=cmp(a[q[x]][0],o); if(t){ flag=1; return; }else{ flag=-1; return; } } int main(){ scanf("%d",&n); if(n==1){ puts("! 1"); fflush(stdout); return 0; } if(n==2){ puts("! 2 1"); fflush(stdout); return 0; } //even //odd m=n/2; //use odd to sort even cnt=1; cp=1; a[1].clear(); for(i=1;i<=m;i++)a[1].push_back(i); q[1]=1; for(i=1;i<=n-m;i++){ int l=1,r=cp; while(l+K<=r){ int mid=(l+r)>>1; gao2(mid,-i); if(flag<0)r=mid;else l=mid; } while(l<=r){ int mid=(l+r)>>1; gao(mid,-i); if(flag==0)break; if(flag<0)r=mid-1;else l=mid+1; } l=max(l-2,1); while(l<=cp){ gao2(l,-i); if(flag>0)l++;else break; } int res=0; for(j=1;j<l;j++)res+=a[q[j]].size(); fin[m+i]=res*2+1; } int k=0; for(i=1;i<=cp;i++){ for(j=0;j<a[q[i]].size();j++){ k+=2; fin[a[q[i]][j]]=k; } } printf("!"); for(i=1;i<=n;i++)printf(" %d",fin[i]); puts(""); fflush(stdout); }
J. Journey from Petersburg to Moscow
若经过至多$k$条边,则答案显然包含在$1$到$n$直接的最短路之中。
否则至少经过$k$条边,枚举最大$k$条边中最小的那一条边的权值$x$,将所有边权值重置为$\max(0,w-x)$,求出$1$到$n$的最短路$d$,则可以用$d+kx$更新答案。这是因为如果此时求出的最短路$d$中边数不够$k$,那么$d+kx$必然不优;而如果收费的边超过$k$条,则减去的$x$过小,也会造成答案不优,因此一定存在一个$x$使得刚好取到最优解。
时间复杂度$O(m^2\log m)$。
#include<cstdio> #include<queue> #include<algorithm> #include<vector> using namespace std; typedef long long ll; typedef pair<ll,int>P; const int N=3010; const ll inf=1LL<<60; int n,m,K,i,g[N],v[N<<1],w[N<<1],nxt[N<<1],ed; int lim,W; ll d[N]; ll ans; priority_queue<P,vector<P>,greater<P> >q; inline void add(int x,int y,int z){ v[++ed]=y; w[ed]=z; nxt[ed]=g[x]; g[x]=ed; } struct E{int x,y,z;}e[N]; inline bool cmp(const E&a,const E&b){return a.z<b.z;} inline void ext(int x,ll y){ if(d[x]>y){ q.push(P(d[x]=y,x)); } } inline void dij(){ int i,j; for(i=1;i<=n;i++)d[i]=inf; ext(1,0); while(!q.empty()){ P t=q.top();q.pop(); if(d[t.second]<t.first)continue; for(int i=g[t.second];i;i=nxt[i]){ ext(v[i],t.first+max(w[i]-W,0)); } } } int main(){ scanf("%d%d%d",&n,&m,&K); for(i=1;i<=m;i++)scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].z); sort(e+1,e+m+1,cmp); ed=1; for(i=1;i<=m;i++)add(e[i].x,e[i].y,e[i].z),add(e[i].y,e[i].x,e[i].z); dij(); ans=d[n]; for(lim=1;lim<=m;lim++){ W=e[lim].z; dij(); ans=min(ans,d[n]+1LL*W*K); } printf("%lld",ans); }
K. Knapsack Cryptosystem
留坑。
L. Laminar Family
首先将所有未被任何一条树链经过的树边删除,那么剩下的点度数都不能超过$2$,否则必然非法。
那么剩下的图每个连通块都只可能是链,是经典序列问题,把所有区间按左端点排序后单调栈判断即可。
时间复杂度$O(f\log n)$。
#include<cstdio> #include<algorithm> #include<cstdlib> using namespace std; const int N=100010; int n,m,i,x,y,g[N],v[N<<1],nxt[N<<1],ed; int f[N],son[N],size[N],top[N],d[N]; int e[N][2]; int tag[N]; int deg[N]; int G[N],V[N<<1],NXT[N<<1],ED; bool vis[N]; int ge[N],vv[N],nxte[N],ee; void NIE(){ puts("No"); exit(0); } inline void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;} inline void adde(int x,int y){vv[++ee]=y;nxte[ee]=ge[x];ge[x]=ee;} void dfs(int x){ d[x]=d[f[x]]+1; size[x]=1; for(int i=g[x];i;i=nxt[i])if(v[i]!=f[x]){ f[v[i]]=x; dfs(v[i]); size[x]+=size[v[i]]; if(size[v[i]]>size[son[x]])son[x]=v[i]; } } void dfs2(int x,int y){ top[x]=y; if(son[x])dfs2(son[x],y); for(int i=g[x];i;i=nxt[i])if(v[i]!=f[x]&&v[i]!=son[x])dfs2(v[i],v[i]); } inline void addedge(int x,int y){ //printf("! %d %d\n",x,y); deg[x]++;deg[y]++; V[++ED]=y;NXT[ED]=G[x];G[x]=ED; V[++ED]=x;NXT[ED]=G[y];G[y]=ED; } void dfs3(int x){ for(int i=g[x];i;i=nxt[i])if(v[i]!=f[x]){ dfs3(v[i]); tag[x]+=tag[v[i]]; } if(x>1&&tag[x])addedge(x,f[x]); } inline int lca(int x,int y){ while(top[x]!=top[y]){ if(d[top[x]]<d[top[y]])swap(x,y); x=f[top[x]]; } return d[x]<d[y]?x:y; } int q[N],pos[N]; int all; struct Seg{ int l,r; }seg[N],st[N]; inline bool cmp(const Seg&a,const Seg&b){ if(a.l!=b.l)return a.l<b.l; return a.r>b.r; } inline void append(int l,int r){ if(l>r)swap(l,r); seg[++all].l=l; seg[all].r=r; } inline void check(){ if(all<=1)return; sort(seg+1,seg+all+1,cmp); int i; int t=0; for(i=1;i<=all;i++){ while(t){ if(seg[i].r<=st[t].r)break; if(seg[i].l<=st[t].r)NIE(); t--; } st[++t]=seg[i]; } } inline void solve(int S){ int cnt=0; int i,j; while(1){ vis[S]=1; q[++cnt]=S; int t=0; for(i=G[S];i;i=NXT[i])if(!vis[V[i]]){ t=V[i]; } if(!t)break; S=t; } //for(i=1;i<=cnt;i++)printf("%d ",q[i]);puts(""); for(i=1;i<=cnt;i++)pos[q[i]]=i; all=0; for(i=1;i<=cnt;i++){ for(j=ge[q[i]];j;j=nxte[j]){ append(pos[q[i]],pos[vv[j]]); } } check(); } int main(){ scanf("%d%d",&n,&m); for(i=1;i<n;i++)scanf("%d%d",&x,&y),add(x,y),add(y,x); dfs(1); dfs2(1,1); for(i=1;i<=m;i++){ scanf("%d%d",&x,&y); e[i][0]=x,e[i][1]=y; int z=lca(x,y); tag[x]++; tag[y]++; tag[z]-=2; adde(x,y); } dfs3(1); for(i=1;i<=n;i++)if(deg[i]>2)NIE(); for(i=1;i<=n;i++)if(deg[i]==1&&!vis[i])solve(i); puts("Yes"); } /* 4 2 1 2 2 3 2 4 1 2 4 2 6 5 1 2 2 3 3 4 5 6 5 2 2 1 6 6 1 4 3 4 4 1 */