2017-2018 ACM-ICPC, NEERC, Moscow Subregional Contest
A. Advertising Strategy
最优策略一定是第一天用$y$元,最后一天再用$x-y$元补满。
枚举所有可能的$y$,然后模拟即可,天数为$O(\log n)$级别。
时间复杂度$O(x\log n)$。
#include<cstdio> typedef long long ll; const ll inf=1LL<<60; ll min(ll a,ll b){return a<b?a:b;} ll cal(ll n,ll x,ll o){ ll ans=1; ll a=0,b,k,pre; while(1){ pre=a; k=min(n-a,x); b=a+k; x-=k; a=b+min(b,(n-b)/2); if(a==n)return ans; if(a+o>=n)return ans+1; if(a==pre)return inf; ans++; } } int main(){ ll n,x; scanf("%lld%lld",&n,&x); ll ans=inf; for(int i=0;i<x;i++)ans=min(ans,cal(n,x-i,i)); printf("%lld",ans); }
B. Byteland Trip
留坑。
C. Carpet
对树进行轻重链剖分,那么把重儿子往右挂,轻儿子往下挂即可。层数$O(\log n)$。
#include<cstdio> const int N=100010,K=25; int n,i,x,y,g[N],v[N<<1],nxt[N<<1],ed,size[N],son[N]; int X[N],Y[N]; int cnt[K]; inline void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;} void dfs(int x,int y){ size[x]=1; for(int i=g[x];i;i=nxt[i])if(v[i]!=y){ dfs(v[i],x); size[x]+=size[v[i]]; if(size[v[i]]>size[son[x]])son[x]=v[i]; } } void dfs2(int x,int y,int z){ Y[x]=z; X[x]=++cnt[z]; for(int i=g[x];i;i=nxt[i])if(v[i]!=y&&v[i]!=son[x])dfs2(v[i],x,z+1); if(son[x])dfs2(son[x],x,z); } int main(){ scanf("%d",&n); for(i=1;i<n;i++)scanf("%d%d",&x,&y),add(x,y),add(y,x); dfs(1,0); dfs2(1,0,1); for(i=1;i<=n;i++)printf("%d %d\n",X[i],Y[i]); }
D. Decoding of Varints
按题意模拟即可。
#include<cstdio> typedef unsigned long long ll; int n,x;ll ans,len; int main(){ scanf("%d",&n); ans=0; len=1; while(n--){ scanf("%d",&x); if(x<128){ ans+=1ULL*x*len; if(ans%2==0){ printf("%llu\n",ans/2); }else{ printf("-%llu\n",ans/2+1); } ans=0; len=1; }else{ ans+=1ULL*(x-128)*len; len*=128; } } }
E. Empire History
留坑。
F. Fake or Leak?
设部分终榜里排名最高的队伍为$A$,最低的为$B$,那么对于每个没出现的队伍,有两种可能:
$1.$最坏情况:封榜后一题也没有通过,检查是否比$B$还靠后。
$2.$最好情况:封榜后立即AK了,检查是否比$A$还靠前。
#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 = 1010, 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, m, k; string s[N], s2[N]; char ch[N][30], ch2[N][30]; int num[N][30], num2[N][30], tim[N][30], tim2[N][30]; int solvenum[N], solvenum2[N], solvetim[N], solvetim2[N], solvelst[N], solvelst2[N]; map<string, int> mop, visit; int compare(int x, int y) // x solve y solve2 { if(solvenum[x] > solvenum2[y]) return 1; else if(solvenum[x] < solvenum2[y]) return -1; if(solvetim[x] > solvetim2[y]) return -1; else if(solvetim[x] < solvetim2[y]) return 1; if(solvelst[x] > solvelst2[y]) return -1; else if(solvelst[x] < solvelst2[y]) return 1; else if(s[x] < s2[y]) return 1; // else return -1; } int main() { scanf("%d%d%d",&k, &n, &m); mop.clear(); MS(solvelst, 0); MS(solvelst2, 0); MS(solvenum, 0); MS(solvenum2, 0); MS(solvetim, 0); MS(solvetim2, 0); for(int i = 1; i <= n; i ++){ //scanf("%s", s[i]); cin >> s[i]; mop[s[i]] = i; for(int j = 1; j <= k; j ++){ scanf(" %c%d%d", &ch[i][j], &num[i][j], &tim[i][j]); //printf("%c %d %d\n", ch[i][j], num[i][j], tim[i][j]); if(ch[i][j] == '+'){ solvenum[i] ++; solvetim[i] += (num[i][j] - 1) * 20 + tim[i][j]; gmax(solvelst[i], tim[i][j]); } } } visit.clear(); for(int i = 1; i <= m; i ++){ //scanf("%s", s2[i]); cin >> s2[i]; visit[s2[i]] = 1; for(int j = 1; j <= k; j ++){ scanf(" %c%d%d", &ch2[i][j], &num2[i][j], &tim2[i][j]); if(ch2[i][j] == '+'){ solvenum2[i] ++; solvetim2[i] += (num2[i][j] - 1) * 20 + tim2[i][j]; gmax(solvelst2[i], tim2[i][j]); } } } if(m == 1){ puts("Leaked"); return 0; } int FLAG = 1; for(int i = 1; i <= n; i ++){ if(visit[s[i]] == 0){ int flag = 0; if(compare(i, 1) > 0 || compare(i, m) < 0) flag = 1; for(int j = 1; j <= k; j ++){ // AK if(ch[i][j] == '-'){ solvenum[i] ++; solvetim[i] += (num[i][j]) * 20 + 240; gmax(solvelst[i], 240); } else if(ch[i][j] == '.'){ solvenum[i] ++; solvetim[i] += 240; gmax(solvelst[i], 240); } } if(compare(i, 1) > 0 || compare(i, m) < 0) flag = 1; if(!flag) FLAG = 0; } } if(FLAG) puts("Leaked"); else puts("Fake"); //scanf("%d", &n); return 0; } /* 【trick&&吐槽】 3 3 2 crabs + 1 1 + 1 2 + 1 3 lions . 0 0 - 5 239 . 0 0 wombats . 0 0 . 0 0 . 0 0 wombats + 1 241 + 3 299 - 22 299 lions + 1 241 + 6 240 - 3 299 3 4 2 crabs + 1 1 + 1 2 + 1 3 lions . 0 0 + 5 239 . 0 0 wolves . 0 0 . 0 0 . 0 0 wombats . 0 0 . 0 0 . 0 0 crabs + 1 1 + 1 2 + 1 3 wombats . 0 0 + 2 299 . 0 0 【题意】 【分析】 【时间复杂度&&优化】 */
G. God of Winds
设$f[i][j]$表示$(i,j)$操作次数,那么可以表示成$A-B=C$的限制,检查是否矛盾即可。
#include<cstdio> #include<iostream> #include<cstdlib> using namespace std; typedef long long ll; const int N=550,M=N*N; int n,m,cnt,i,j,id[N][N],x,A,B,g[M],v[M*4],w[M*4],nxt[M*4],ed; bool vis[M]; ll f[M]; inline void add(int x,int y,int z){ v[++ed]=y;w[ed]=z;nxt[ed]=g[x];g[x]=ed; //printf("%d %d %d\n",x,y,z); }//x - y = z void dfs(int x,ll y){ if(vis[x]){ if(f[x]!=y){ puts("No"); exit(0); } return; } vis[x]=1; f[x]=y; for(int i=g[x];i;i=nxt[i]){ dfs(v[i],f[x]-w[i]); } } int main(){ scanf("%d%d",&n,&m); for(i=0;i<n;i++)for(j=0;j<m;j++)id[i][j]=++cnt; for(i=0;i<n;i++)for(j=0;j<m;j++){ B=id[i][j]; scanf("%d",&x); A=id[(i-1+n)%n][j]; add(B,A,-x); add(A,B,x); scanf("%d",&x); A=id[i][(j-1+m)%m]; add(B,A,x); add(A,B,-x); } for(i=1;i<=cnt;i++)if(!vis[i])dfs(i,0); puts("Yes"); }
H. Hilarious Cooking
可行的$T$是一个区间,求出$T$的最小值和最大值即可。
#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 = 1e5 + 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; LL T, n, m; LL a[N], b[N]; LL cnt(LL a, LL b) { //a should <= b if(a > b)return 0; if(b < 0)return 0; LL num = (b - a + 1); LL negnum = a < 0 ? abs(a) : 0; LL sub = (1 + negnum) * negnum / 2; return (a + b) * num / 2 + sub; } bool check() { LL BOT = 0; LL TOP = 0; for(int i = 1; i <= m; ++i) { BOT += b[i]; TOP += b[i]; } for(int i = 1; i < m; ++i) { LL pos_dif = a[i + 1] - a[i]; LL val_dif = abs(b[i + 1] - b[i]); LL mx = max(b[i + 1], b[i]); LL mn = min(b[i + 1], b[i]); if(val_dif > pos_dif) { // //puts("TOO MUCH VAL_DIF"); // return 0; } if(pos_dif == 1)continue; LL top = mx + (pos_dif - val_dif) / 2; LL bot = mn - (pos_dif - val_dif) / 2; if(val_dif % 2 == pos_dif % 2) { TOP += cnt(mx, top) + cnt(mn, top) - max(top, 0ll); BOT += cnt(bot, mx) + cnt(bot, mn) - max(bot, 0ll); } else { TOP += cnt(mx, top) + cnt(mn, top); BOT += cnt(bot, mx) + cnt(bot, mn); } TOP -= mn + mx; BOT -= mn + mx; } { //1: LL top = b[1] + (a[1] - 1); LL bot = b[1] - (a[1] - 1); TOP += cnt(b[1] + 1, top); BOT += cnt(bot, b[1] - 1); // //printf("pretop = %lld, prebot = %lld\n", top, bot); // } { //m: LL top = b[m] + (n - a[m]); LL bot = b[m] - (n - a[m]); TOP += cnt(b[m] + 1, top); BOT += cnt(bot, b[m] - 1); // //printf("subtop = %lld, subbot = %lld\n", top, bot); // } // //printf("TOP = %lld, BOT = %lld\n", TOP, BOT); // return BOT <= T && TOP >= T; } int main() { while(~scanf("%lld%lld%lld",&T, &n, &m)) { for(int i = 1; i <= m; ++i) { scanf("%lld%lld", &a[i], &b[i]); } puts(check() ? "Yes" : "No"); } return 0; } /* 【trick&&吐槽】 【题意】 【分析】 【时间复杂度&&优化】 */
I. Infinite Gift
将每个向量模$2$后压位高斯消元检查是否有解即可。时间复杂度$O(\frac{nk^2}{64})$。
#include<cstdio> #include<bitset> using namespace std; const int N=1510; int n,m,i,j,k,x;bitset<N>g[N]; int main(){ scanf("%d%d",&n,&m); for(i=1;i<=n;i++){ for(j=1;j<=m;j++){ scanf("%d",&x); if(x%2)g[i][j]=1; } g[i][0]=1; } for(i=j=1;i<=m;i++){ for(k=j;k<=n;k++)if(g[k][i])break; if(k>n)continue; swap(g[k],g[j]); for(k=j+1;k<=n;k++)if(g[k][i])g[k]^=g[j]; j++; } for(;j<=n;j++)if(g[j][0])return puts("No"),0; puts("Yes"); }
J. Judging the Trick
取一条水平线,做纵向的一维线段覆盖,若有解则找到了一组可行解。
否则注意到三角形总面积小于矩形面积,故求出水平线左右侧的三角形总面积和矩形面积,根据这个条件判断哪边存在解,然后缩小范围即可。
时间复杂度$O(n\log n\log w)$。
#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 = 1e5 + 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; const long double eps = 1e-11; int sgn(long double x) { if(fabs(x) < eps) return 0; return x > 0 ? 1 : -1; } long double sqr(long double x){return x * x;} struct point { long double x, y; point(){} point(long double a, long double b):x(a), y(b) {} friend point operator + (const point &a, const point &b){ return point(a.x + b.x, a.y + b.y); } friend point operator - (const point &a, const point &b){ return point(a.x - b.x, a.y - b.y); } friend bool operator == (const point &a, const point &b){ return sgn(a.x - b.x) == 0 && sgn(a.y - b.y) == 0; } friend point operator * (const point &a, const long double &b){ return point(a.x * b, a.y * b); } friend point operator * (const long double &a, const point &b){ return point(a * b.x, a * b.y); } friend point operator / (const point &a, const long double &b){ return point(a.x / b, a.y / b); } long double norm(){ return sqrt(sqr(x) + sqr(y)); } }; long double det(const point &a, const point &b) { return a.x * b.y - a.y * b.x; } long double dot(const point &a, const point &b) { return a.x * b.x + a.y * b.y; } struct line { point a, b; line(){} line(point x, point y) : a(x), b(y) {} }; bool PointOnSegment(point p, point s, point t) { return sgn(det(p - s, t - s)) == 0 && sgn(dot(p - s, p - t)) <= 0; } bool parallel(line a, line b) { return !sgn(det(a.a - a.b, b.a - b.b)); } bool line_make_point(line a, line b, point &res) { if(parallel(a, b)) return false; long double s1 = det(a.a - b.a, b.b - b.a); long double s2 = det(a.b - b.a, b.b - b.a); res = (s1 * a.b - s2 * a.a) / (s1 - s2); return true; } bool cmp(line l1, line l2) { return l1.a.y < l2.a.y; } long double area(point o, point a, point b) { return fabs((a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x)); } int m, k; point a[N][4]; line seg[N], SEG[N]; bool check(long double mid, long double &larea, long double &rarea) { line ll; ll.a = point(mid, 0), ll.b = point(mid, m); int segn = 0; for(int i = 1; i <= k; i ++){ int num = 0; point tmp[10]; for(int j = 0; j < 3; j ++){ point res; if(line_make_point(ll, line(a[i][j], a[i][j + 1]), res) && PointOnSegment(res, a[i][j], a[i][j + 1])){ tmp[++ num] = res; } } if(num) { long double miny = 1e9, maxy = 0; for(int i = 1; i <= num; i ++){ gmin(miny, tmp[i].y); gmax(maxy, tmp[i].y); } tmp[1].x = mid, tmp[1].y = miny; tmp[2].x = mid, tmp[2].y = maxy; seg[++ segn].a = tmp[1], seg[segn].b = tmp[2]; if(sgn(tmp[1].y - tmp[2].y) == 0){ if(sgn(a[i][0].x - mid) < 0 || sgn(a[i][1].x - mid) < 0 || sgn(a[i][2].x - mid) < 0) larea += area(a[i][0], a[i][1], a[i][2]); else rarea += area(a[i][0], a[i][1], a[i][2]); } else if(sgn(a[i][0].x - mid) == 0 || sgn(a[i][1].x - mid) == 0 || sgn(a[i][2].x - mid) == 0){ int lnum = 0, rnum = 0; point pl[4], pr[4]; for(int j = 0; j < 3; j ++){ if(sgn(a[i][j].x - mid) < 0) pl[++ lnum] = a[i][j]; if(sgn(a[i][j].x - mid) > 0) pr[++ rnum] = a[i][j]; } if(lnum == 1 && rnum == 1){ larea += area(tmp[1], tmp[2], pl[1]); rarea += area(tmp[1], tmp[2], pr[1]); } else if(lnum == 1) larea += area(a[i][0], a[i][1], a[i][2]); else rarea += area(a[i][0], a[i][1], a[i][2]); } else{ int lnum = 0, rnum = 0; point pl[4], pr[4]; for(int j = 0; j < 3; j ++){ if(sgn(a[i][j].x - mid) < 0) pl[++ lnum] = a[i][j]; if(sgn(a[i][j].x - mid) > 0) pr[++ rnum] = a[i][j]; } if(lnum == 1){ long double tt = area(pl[1], tmp[1], tmp[2]); larea += tt; rarea += area(a[i][0], a[i][1], a[i][2]) - tt; } else{ long double tt = area(pr[1], tmp[1], tmp[2]); rarea += tt; larea += area(a[i][0], a[i][1], a[i][2]) - tt; } } } else{ if(sgn(a[i][0].x - mid) < 0) larea += area(a[i][0], a[i][1], a[i][2]); else rarea += area(a[i][0], a[i][1], a[i][2]); } } // 合并线段 sort(seg + 1, seg + segn + 1, cmp); long double r = 0; for(int i = 1; i <= segn; i ++){ if(i==1&&sgn(seg[i].a.y)>0){ double xx = mid; double yy = 0; printf("%.8f %.8f\n", xx, yy); return 1; } if(sgn(seg[i].a.y - r) > 0){ double xx = mid; double yy = (seg[i].a.y+r)/2; printf("%.8f %.8f\n", xx, yy); return 1; } r = max(r,seg[i].b.y); } if(sgn(r-m)<0){ double xx = mid; double yy = m; printf("%.8f %.8f\n", xx, yy); return 1; } return 0; } int main() { scanf("%d%d%d", &k, &n, &m); for(int i = 1; i <= k; i ++){ for(int j = 0; j < 3; j ++){ double xx, yy; scanf("%lf%lf", &xx, &yy); a[i][j].x = xx; a[i][j].y = yy; } a[i][3] = a[i][0]; } long double l = 0, r = n; while(1){ long double mid = (l + r) / 2; long double larea = 0, rarea = 0; if(check(mid, larea, rarea)){ // 可以的话在里面输出 break; } else{ if(larea - mid*2.0*m < -1e-6) r = mid; else l = mid; } } return 0; } /* 【trick&&吐槽】 5 4 3 0 0 3 0 0 2 3 3 0 1 0 3 1 1 3 1 2 3 3 0 4 0 4 3 4 3 3 2 4 1 【题意】 【分析】 【时间复杂度&&优化】 */