XV Open Cup named after E.V. Pankratiev. GP of Three Capitals
A. Add and Reverse
要么全部都选择$+1$,要么加出高$16$位后翻转位序然后再补充低$16$位。
#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 = 1<<16, 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 n; int f[N]; int main() { scanf("%lld",&n); LL A=n>>16; LL B=n&65535; LL ans=B; if(A)ans++; for(LL i=0;i<16;i++)if(A>>(15-i)&1)ans+=1LL<<i; printf("%lld",min(ans,n)); return 0; if(0)while(~scanf("%lld",&n)) { LL top = 1ll << 32; LL i = n; LL w = printf("%lld\n", w); } //return 0; { MS(f, 63); f[0] = 0; queue<int>q; q.push(0); int top = 1ll << 16; while(!q.empty()) { int x = q.front(); q.pop(); int y = x + 1; if(y <= top && f[x] + 1 < f[y]) { f[y] = f[x] + 1; q.push(y); } int z = top - x; if(f[x] + 1 < f[z]) { f[z] = f[x] + 1; q.push(y); } } for(int i = 0; i < top; ++i) { int w = i <= top / 2 ? i : top + 1 - i; printf("%d: %d %d\n", i, f[i], w); if(w != f[i]) { puts("no!!!"); while(1); } } } return 0; } /* 【trick&&吐槽】 【题意】 【分析】 【时间复杂度&&优化】 */
B. Analyze This
bitset加速暴力。
#include<cstdio> const int N=400010; int n,m,mx,lim,i,j,x,a[N],f[N][2];unsigned long long v[64][N/64+5]; inline void solve(int d){ int i,j,A=d>>6,B=d&63; for(i=0;i+A<=lim;i++)if(v[0][i]&v[B][i+A])for(j=i<<6;;j++)if(a[j]&&a[j+d]){ f[d][0]=a[j]; f[d][1]=a[j+d]; return; } } int main(){ scanf("%d%d",&n,&m); for(i=1;i<=n;i++){ scanf("%d",&x); a[x]=i; if(x>mx)mx=x; } lim=mx/64; for(i=0;i<=mx;i++)if(a[i])for(j=0;j<64&&j<=i;j++)v[j][(i-j)>>6]|=1ULL<<((i-j)&63); for(f[0][1]=i=1;i<=mx;i++){ f[i][0]=f[i-1][0]; f[i][1]=f[i-1][1]; solve(i); } while(m--){ scanf("%d",&x); if(x>mx)x=mx; printf("%d %d\n",f[x][0],f[x][1]); } }
C. Bipartite Graph
每个点向附近$3$个点连边即可。
#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 T; int main() { scanf("%d",&T); while(T--) { int n; scanf("%d", &n); printf("%d\n", (n - 2) * 3); for(int d = 0; d <= 2; ++d) { for(int i = 0; i < n - 2; ++i) { printf("%d %d\n", d + i, i); } } } return 0; } /* 【trick&&吐槽】 【题意】 【分析】 【时间复杂度&&优化】 */
D. Bridge Building
二分答案,那么对于$x$个物品$a$,$y$的数量是定的,设$f[i][j]$表示$i$个$a$和$j$个$b$最多拼出几列,按性价比从小到大背包,一旦有解则返回。
时间复杂度$O(n^3\log n)$。
#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; const int N=505; int X,A,Y,B,L,mid,f[N][N]; inline void up(int&a,int b){a<b?(a=b):0;} pair<int,int>a[N]; bool cmp(pair<int,int> a, pair<int,int> b) { int val1 = a.first * A + a.second * B; int val2 = b.first * A + b.second * B; return val1 < val2; } inline bool check(){ int i,j,k; for(i=0;i<=X;i++)for(j=0;j<=Y;j++)f[i][j]=0; int g = 0; int pre=10000000; for(i=0;i<=X;i++){ //A*i+B*j>=mid int ret=mid-A*i; int tmp=0; if(ret>0)tmp=ret/B+((ret%B)>0); if(tmp>=pre)continue; pre=tmp; if(tmp>Y)continue; a[++g].first = i; a[g].second = tmp; if(ret <= 0)break; } //x++, y-- /* for(int j = X; j >= 0; --j) { for(int k = Y; k >= 0; --k) { int st = g + 1; int l = 1; int r = g; while(l <= r) { int mid = (l+r)/2; if(a[mid].second <= k) { st = mid; r = mid - 1; } else l = mid + 1; } if(st > g || a[st].first > j)break; if(f[j][k] >= L - 1)return 1; for(int i = st; i <= g && a[i].first <= j; ++i) { up(f[j - a[i].first][k - a[i].second], f[j][k] + 1); } } } */ sort(a + 1, a + g + 1, cmp); for(int o=1;o<=g;++o){ int i = a[o].first; int tmp = a[o].second; for(j=X;j>=i;j--)for(k=Y;k>=tmp;k--){ if(f[j][k]>=L-1)return 1; up(f[j-i][k-tmp],f[j][k]+1); } } //for(i=0;i<=X;i++)for(j=0;j<=Y;j++)if(f[i][j]>=L)return 1; return 0; } int main(){ while(~scanf("%d%d%d%d%d",&X,&A,&Y,&B,&L)){ int l=1,r=(A*X+Y*B)/L,ans=0; while(l<=r){ mid=(l+r)>>1; if(check())l=(ans=mid)+1;else r=mid-1; } printf("%d\n",ans); } } /* 1 1 1 1 1 */
E. Child’s Game with Robot
顺时针走一圈,若目的地在中心则回来,否则在目的地两侧来回挪动,若奇偶性不同则原地不动一次进行调整。
#include<cstdio> int movenorth(){//founded? puts("move north"); fflush(stdout); char s[100]; scanf("%s",s); return s[0]=='f'; } int movewest(){//founded? puts("move west"); fflush(stdout); char s[100]; scanf("%s",s); return s[0]=='f'; } int moveeast(){//founded? puts("move east"); fflush(stdout); char s[100]; scanf("%s",s); return s[0]=='f'; } int movesouth(){//founded? puts("move south"); fflush(stdout); char s[100]; scanf("%s",s); return s[0]=='f'; } int stay(){//founded? puts("echo Ready!"); fflush(stdout); char s[100]; scanf("%s",s); return s[0]=='f'; } int main(){ if(movenorth()){ stay(); for(int i=1;i<=4;i++){ movesouth(); movenorth(); } return 0; } if(movewest()){ for(int i=1;i<=4;i++){ moveeast(); movewest(); } return 0; } if(movesouth()){ stay(); for(int i=1;i<=3;i++){ movesouth(); movenorth(); } return 0; } if(movesouth()){ for(int i=1;i<=3;i++){ movenorth(); movesouth(); } return 0; } if(moveeast()){ stay(); for(int i=1;i<=2;i++){ movenorth(); movesouth(); } return 0; } if(moveeast()){ for(int i=1;i<=2;i++){ movenorth(); movesouth(); } return 0; } if(movenorth()){ stay(); movenorth(); movesouth(); return 0; } if(movenorth()){ movesouth(); movenorth(); return 0; } movesouth(); movewest(); return 0; }
F. Quadruples of Points
给每个集合一个unsigned long long的随机权值,扫描线+树状数组查询矩形内所有点的权值和即可。
#include<cstdio> #include<algorithm> using namespace std; typedef unsigned long long ll; const int N=2000010; ll ran,goal; int n,m,i,j,ce; int a[N],cnt; ll bit[N],ans[N]; struct P{ int x,l,r,t;ll v; P(){} P(int _x,int _l,int _r,int _t,ll _v){x=_x,l=_l,r=_r,t=_t,v=_v;} }e[N]; inline bool cmp(const P&a,const P&b){ return a.x!=b.x?a.x<b.x:a.t<b.t; } inline void ins(int x,ll p){for(;x<=cnt;x+=x&-x)bit[x]+=p;} inline ll ask(int x){ll t=0;for(;x;x-=x&-x)t+=bit[x];return t;} inline ll sum(int l,int r){return ask(r)-ask(l-1);} int main(){ scanf("%d%d",&n,&m); for(i=1;i<=n;i++){ ran=ran*233+17; goal+=ran; goal+=ran; for(j=0;j<4;j++){ int x,y; scanf("%d%d",&x,&y); e[++ce]=P(x,y,0,0,ran); a[++cnt]=y; } } for(i=1;i<=m;i++){ int x1,y1,x2,y2; scanf("%d%d%d%d",&x1,&y1,&x2,&y2); e[++ce]=P(x2,y1,y2,1,i); e[++ce]=P(x1-1,y1,y2,2,i); a[++cnt]=y1; a[++cnt]=y2; } sort(a+1,a+cnt+1); sort(e+1,e+ce+1,cmp); for(i=1;i<=ce;i++){ if(e[i].t==0){ ins(lower_bound(a+1,a+cnt+1,e[i].l)-a,e[i].v); } if(e[i].t==1){ ans[e[i].v]+=sum(lower_bound(a+1,a+cnt+1,e[i].l)-a,lower_bound(a+1,a+cnt+1,e[i].r)-a); } if(e[i].t==2){ ans[e[i].v]-=sum(lower_bound(a+1,a+cnt+1,e[i].l)-a,lower_bound(a+1,a+cnt+1,e[i].r)-a); } } for(i=1;i<=m;i++)puts(ans[i]==goal?"YES":"NO"); } /* 2 3 0 0 0 1 1 0 1 2 2 0 2 -1 2 -2 2 -3 0 -1 2 0 0 -1 2 1 0 0 0 1 1 1 0 0 0 1 1 0 1 2 0 -1 2 0 */
G. Mosaic Tracery
找到度数为$2$的点作为角落然后开始构造。
H. List of Powers
若$r-l$比较小,那么可以枚举其中每个数,用BSGS检查。
否则$r-l$比较大,因为$a^k\bmod p$分布非常随机,而答案不超过$100$个,故周期很短,暴力枚举循环节内所有$k$即可。
注意BSGS检查次数比较多,可以通过增加步长,牺牲预处理复杂度来减少每次查询的复杂度。
#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> #include<tr1/unordered_set> using namespace std; using namespace std::tr1; 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 pow_mod(LL x, int y, int Z) { LL ans = 1; while(y){ if(y & 1) ans = ans * x % Z; y >>= 1; x = x * x % Z; } return ans; } int s; unordered_set<int> rec; int cur[N], powmod[N]; void init(int m, int x) { s = (int) (sqrt((double)m)); for(; (LL) s * s <= m;) s ++; int Cur = 1; for(int i = 0; i < s; i ++){ rec.insert(Cur); Cur = 1LL * Cur * x % m; } int mul = Cur; cur[0] = 1; powmod[0] = pow_mod(cur[0], m - 2, m); for(int i = 1; i < s; i ++){ cur[i] = 1LL * cur[i - 1] * mul % m; powmod[i] = pow_mod(cur[i], m - 2, m); } } LL discrete_log(int x, int n, int m) { for(int i = 0; i < s; i ++){ int more = 1LL * n * powmod[i] % m; if(rec.find(more)!=rec.end()){ return 1; } } return -1; } int P, A, L, R; set<int>sot; set<int> :: iterator it; inline LL po(LL a,LL b,LL p){ b=(b%(p-1)+p-1)%(p-1); a%=p; LL t=1; for(;b;b>>=1LL,a=a*a%p)if(b&1)t=t*a%p; return t; } int main() { scanf("%d%d%d%d", &P, &A, &L, &R); int phi=P-1; int per=phi; for(int i=1;i*i<=phi;i++)if(phi%i==0){ if(po(A,i,P)==1)per=min(per,(int)(i)); if(po(A,phi/i,P)==1)per=min(per,(int)(phi/i)); } sot.clear(); int lim = 1e8; if(R-L>2000){ LL t = 1; for(int i = 1; i <= per; i ++){ t = t * A; if(t==1)break; if(t >= P) t %= P; if(t <= R && t >= L){ sot.insert(t); } //if(sot.size() >= 100) break; } for(it = sot.begin(); it != sot.end(); it ++){ printf("%d ", *it); }puts(""); } else{ vector<int> sot; init(P, A); for(int i = L; i <= R; i ++){ if(discrete_log(A, i, P) != -1) sot.push_back(i); //if(sot.size() >= 100) break; } for(auto it = sot.begin(); it != sot.end(); it ++){ printf("%d ", *it); }puts(""); } return 0; } /* 【trick&&吐槽】 【题意】 【分析】 【时间复杂度&&优化】 */
I. Potential Well
答案就是边权平均数最小的环,求出答案后就是差分约束系统模型,Bellman-Ford即可。
对于边权平均数最小的环,设$f[i][j]$表示经过$i$条边到达$j$的最短路,则:
\[ans=\min_{i=1}^n\{\max_{j=0}^{n-1}\frac{f[n][i]-f[j][i]}{n-j}\}\]
时间复杂度$O(nm)$。
#include<cstdio> typedef long long ll; const int N=1005,M=100005; const double inf=1e18; int n,m,i,j,u[M],v[M];double w[M],f[N][N],d[N],ans=inf,now,tmp; inline void up(double&a,double b){a>b?(a=b):0;} int main(){ scanf("%d%d",&n,&m); for(i=1;i<=m;i++)scanf("%d%d%lf",&u[i],&v[i],&w[i]); for(i=1;i<=n;i++)for(j=1;j<=n;j++)f[i][j]=inf; for(i=0;i<n;i++)for(j=1;j<=m;j++)up(f[i+1][v[j]],f[i][u[j]]+w[j]); for(i=1;i<=n;i++)if(f[n][i]<inf/2){ now=-inf; for(j=0;j<n;j++)if(f[j][i]<inf/2){ tmp=1.0*(f[n][i]-f[j][i])/(n-j); if(now<tmp)now=tmp; } up(ans,now); } if(ans>inf/2)return puts("+inf"),0; for(i=1;i<=n;i++)for(j=1;j<=m;j++)up(d[v[j]],d[u[j]]+w[j]-ans); printf("%.10f\n",ans); for(i=1;i<=n;i++)printf("%.10f ",d[i]); }
J. Steiner Tree in Random Graph
留坑。
K. Rotation Transformation
列方程推公式即可,取精度误差最小的作为解。
#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; double a[4][4], b[4][4]; const double PI = acos(-1.0), eps = 1e-8; int sgn(double x) { if(fabs(x) < eps) return 0; return x > 0 ? 1 : -1; } double th, sinth, costh, ux, uy, uz; int main() { /* scanf("%lf", &th); th = th / 180 * PI; scanf("%lf%lf%lf", &ux, &uy, &uz); sinth = sin(th); costh = cos(th); b[1][1] = costh + ux * ux * (1 - costh); b[1][2] = ux * uy * (1 - costh) - uz * sinth; b[1][3] = ux * uz * (1 - costh) + uy * sinth; b[2][1] = uy * ux * (1 - costh) + uz * sinth; b[2][2] = costh + uy * uy * (1 - costh); b[2][3] = uy * uz * (1 - costh) - ux * sinth; b[3][1] = uz * ux * (1 - costh) - uy * sinth; b[3][2] = uz * uy * (1 - costh) + ux * sinth; b[3][3] = costh + uz * uz * (1 - costh); for(int i = 1; i <= 3; i ++){ for(int j = 1; j <= 3; j ++){ //if(sgn(a[i][j] - b[i][j])){ //printf("%d %d %lf %lf", i, j, a[i][j], b[i][j]); //} } } */ for(int i = 1; i <= 3; i ++){ for(int j = 1; j <= 3; j ++){ scanf("%lf", &a[i][j]); //a[i][j] = b[i][j]; } } uz = (a[2][1] - a[1][2]) / 2; uy = (a[1][3] - a[3][1]) / 2; ux = (a[3][2] - a[2][3]) / 2; sinth = uz * uz + uy * uy + ux * ux; sinth = sqrt(sinth); th = asin(sinth); if(sgn(sinth)){ uz /= sinth; uy /= sinth; ux /= sinth; } else{ ux = 0; uy = 0; uz = 1; } double t[5]; t[1] = th; t[2] = -th; t[3] = PI - th; t[4] = th - PI; double differ = 1e9; int oo = 1; for(int o = 1; o <= 4; o ++){ double tmp = 0; sinth = sin(t[o]); costh = cos(t[o]); b[1][1] = costh + ux * ux * (1 - costh); b[1][2] = ux * uy * (1 - costh) - uz * sinth; b[1][3] = ux * uz * (1 - costh) + uy * sinth; b[2][1] = uy * ux * (1 - costh) + uz * sinth; b[2][2] = costh + uy * uy * (1 - costh); b[2][3] = uy * uz * (1 - costh) - ux * sinth; b[3][1] = uz * ux * (1 - costh) - uy * sinth; b[3][2] = uz * uy * (1 - costh) + ux * sinth; b[3][3] = costh + uz * uz * (1 - costh); for(int i = 1; i <= 3; i ++){ for(int j = 1; j <= 3; j ++){ tmp += fabs(a[i][j] - b[i][j]); } } //printf("tmp %d %.10f\n", o, tmp); if(sgn(tmp - differ) < 0){ differ = tmp; oo = o; } } th = t[oo]; //printf("differ = %.10f\n", differ); th = th / PI * 180; printf("%.10f\n", th); printf("%.10f %.10f %.10f\n", ux, uy, uz); return 0; } /* 【trick&&吐槽】 【题意】 【分析】 【时间复杂度&&优化】 */