2017-2018 ACM-ICPC Pacific Northwest Regional Contest (Div. 1)
A. Odd Palindrome
所有回文子串长度都是奇数等价于不存在长度为$2$的偶回文子串,即相邻两个字符都不同。
#include<cstdio> #include<cstring> char a[1111111];int n,i; int main(){ scanf("%s",a+1); n=strlen(a+1); for(i=1;i<n;i++)if(a[i]==a[i+1])return puts("Or not."),0; puts("Odd."); }
B. Enlarging Enthusiasm
注意到方案数不超过$(n-1)\times (n-1)!$,爆搜出所有可行方案即可,需要大量常数优化。
#include<cstdio> #include<algorithm> using namespace std; const int N=15; int n,m,i,a[N],f[(1<<12)+5],ans; int cnt[(1<<12)+5]; int w[N]; void dfs(int S,int mx,int q,int x){ if(!S){ ans++; return; } mx++; if((S&-S)==S){ int j=f[S]; int nowq=mx-j>q?mx-j:q; if(x>=nowq)ans++; return; } int U=S; while(U){ int i=U&-U,j=f[i]; int nowq=mx-j>q?mx-j:q; if(x>=nowq)dfs(S^i,j+nowq,nowq,x-nowq); U^=i; } } void gao(int S,int mx,int q,int x){ if(!S){ ans++; return; } mx++; for(int U=S;U;U-=U&-U){ int i=U&-U; if(i==(1<<(n-1)))continue; int nowq=mx-f[i]; if(nowq<q)nowq=q; if(x>=nowq)dfs(S^i,f[i]+nowq,nowq,x-nowq); } } int main(){ scanf("%d%d",&n,&m); for(i=1;i<1<<n;i++)cnt[i]=cnt[i>>1]+(i&1); for(w[0]=i=1;i<=n;i++)w[i]=w[i-1]*i; for(i=0;i<n;i++)scanf("%d",&a[i]); sort(a,a+n); for(i=0;i<n;i++)f[1<<i]=a[i]; gao((1<<n)-1,a[n-1],0,m); printf("%d",ans); } /* 12 700 1 2 3 4 5 6 7 8 9 10 11 12 */
C. Fear Factoring
考虑每个约数的贡献,那么分段等差数列求和即可。
时间复杂度$O(\sqrt{b})$。
#include<cstdio> typedef long long ll; ll cal(ll n){ ll i,j; ll ans=0; for(i=1;i<=n;i=j+1){ j=n/(n/i); ans+=(i+j)*(j-i+1)*(n/i); } return ans; } int main(){ ll a,b; scanf("%lld%lld",&a,&b); printf("%lld",(cal(b)-cal(a-1))/2); }
D. Rainbow Roads
对于每个点,若其有超过一条同色的边,那么对应子树都不是Good点,差分前缀和打标记即可。
#include<cstdio> #include<algorithm> using namespace std; typedef pair<int,int>P; const int N=300010; int n,i,j,k,x,y,z,g[N],v[N],w[N],nxt[N],ed; int f[N],dfn,st[N],en[N]; int m,ans,s[N]; P q[N]; inline void add(int x,int y,int z){ v[++ed]=y; w[ed]=z; nxt[ed]=g[x]; g[x]=ed; } void dfs(int x,int y){ st[x]=++dfn; f[x]=y; for(int i=g[x];i;i=nxt[i])if(v[i]!=y)dfs(v[i],x); en[x]=dfn; } int main(){ scanf("%d",&n); for(i=1;i<n;i++){ scanf("%d%d%d",&x,&y,&z); add(x,y,z),add(y,x,z); } dfs(1,0); for(i=1;i<=n;i++){ m=0; for(j=g[i];j;j=nxt[j])q[++m]=P(w[j],v[j]); sort(q+1,q+m+1); for(j=1;j<=m;j=k){ for(k=j;k<=m&&q[j].first==q[k].first;k++); if(k>j+1){ for(x=j;x<k;x++){ y=q[x].second; if(y==f[i]){ s[1]++; s[st[i]]--; s[en[i]+1]++; }else{ s[st[y]]++; s[en[y]+1]--; } } } } } for(i=1;i<=n;i++)s[i]+=s[i-1]; for(i=1;i<=n;i++)if(!s[st[i]])ans++; printf("%d\n",ans); for(i=1;i<=n;i++)if(!s[st[i]])printf("%d\n",i); }
E. Straight Shot
二分水平分速度,检查最终是否走到了$(x,0)$。
#include<cstdio> #include<cmath> #include<algorithm> using namespace std; const int N=111; int n,i; double X,V,dx,dy; double lim,goal=1e100,tmp,L,R,MID,l[N],r[N],v[N]; double cal(double dy){ double dx=sqrt(V*V-dy*dy); double x=0,y=0; for(int i=1;i<=n;i++){ y+=dy*(l[i]-x); y+=(dy+v[i])*(r[i]-l[i]); x=r[i]; } y+=dy*(X-x); tmp=X/dx; return y/dx; } int main(){ scanf("%d%lf%lf",&n,&X,&V); for(i=1;i<=n;i++)scanf("%lf%lf%lf",&l[i],&r[i],&v[i]); L=-V,R=V; for(int _=1000;_;_--){ MID=(L+R)/2; if(cal(MID)<0)L=MID;else R=MID; } lim=X/V*2; L=(L+R)/2; if(fabs(cal(L))<1e-8){ cal(L); goal=tmp; } if(goal>lim+1e-8)puts("Too hard"); else printf("%.3f",goal); }
F. Distinct Distances
答案点只可能取在每个点、两个点的中点,以及两对点垂直平分线的交点。
#include<cstdio> #include<cmath> #include<algorithm> using namespace std; const int N=50; const double eps=1e-9; inline int sgn(double x){ if(x>eps)return 1; if(x<-eps)return -1; return 0; } struct P{ double x,y; P(){} P(double _x,double _y){x=_x,y=_y;} P operator+(P b){return P(x+b.x,y+b.y);} P operator-(P b){return P(x-b.x,y-b.y);} P operator*(double b){return P(x*b,y*b);} P operator/(double b){return P(x/b,y/b);} void read(){scanf("%lf%lf",&x,&y);} double len(){return x*x+y*y;} double dis(const P&b){return (x-b.x)*(x-b.x)+(y-b.y)*(y-b.y);} bool operator==(P b){return !sgn(x-b.x)&&!sgn(y-b.y);} P rot90(){return P(-y,x);} }a[N],e[N*N][2]; int n,m,i,j,ans=N; double d[N]; inline void solve(P O){ //printf("%.10f %.10f\n",O.x,O.y); int i; for(i=1;i<=n;i++)d[i]=O.dis(a[i]); sort(d+1,d+n+1); int t=1; for(i=2;i<=n&&t<ans;i++)if(d[i]>d[i-1]+eps)t++; if(t<ans)ans=t; } inline void makeline(P a,P b){ if(a==b)return; P c=(a+b)/2.0; solve(c); b=b-a; e[++m][0]=c; e[m][1]=c+b.rot90(); //printf("%.5f %.5f %.5f %.5f\n",e[m][0].x,e[m][0].y,e[m][1].x,e[m][1].y); } inline double cross(P a,P b){ return a.x*b.y-a.y*b.x; } inline void gao(P a,P b,P p,P q){ double U=cross(p-a,q-p),D=cross(b-a,q-p); if(!sgn(D))return; //puts("!"); solve(a+(b-a)*(U/D)); } int main(){ scanf("%d",&n); for(i=1;i<=n;i++)a[i].read(); for(i=1;i<=n;i++)solve(a[i]); for(i=1;i<=n;i++)for(j=1;j<i;j++){ makeline(a[i],a[j]); } //makeline(a[4],a[5]); //makeline(a[5],a[1]); for(i=1;i<=m;i++)for(j=1;j<i;j++)gao(e[i][0],e[i][1],e[j][0],e[j][1]); printf("%d",ans); } /* 6 0 -5 1 0 -1 0 2 3 3 2 -3 0 */
G. Security Badge
对区间离散化,那么只需要检查$O(m)$个人是否可行,每次暴力Floodfill判断即可。
时间复杂度$O(m(n+m))$。
#include<cstdio> #include<algorithm> using namespace std; const int N=10010; int n,m,k,S,T,i,j,cnt,q[N],K; int g[N],ed,v[N],vc[N],vd[N],nxt[N],vis[N],ans; inline void add(int x,int y,int c,int d){ v[++ed]=y; vc[ed]=c; vd[ed]=d; nxt[ed]=g[x]; g[x]=ed; } void dfs(int x){ if(vis[x])return; vis[x]=1; for(int i=g[x];i;i=nxt[i])if(vc[i]<=K&&K<=vd[i])dfs(v[i]); } int main(){ scanf("%d%d%d%d%d",&n,&m,&k,&S,&T); for(i=1;i<=m;i++){ int a,b,c,d; scanf("%d%d%d%d",&a,&b,&c,&d); add(a,b,c,d); q[++cnt]=c-1; q[++cnt]=d; } sort(q+1,q+cnt+1); for(i=2;i<=cnt;i++)if(q[i]!=q[i-1]){ K=q[i]; for(j=1;j<=n;j++)vis[j]=0; dfs(S); if(vis[T])ans+=q[i]-q[i-1]; } printf("%d",ans); }
H. Avoiding Airports
将每架飞机拆成上飞机和下飞机两个事件,并按照时间顺序依次考虑。
设$dp[x]$表示最后乘坐第$x$架飞机的最小代价,那么转移是经典斜率优化形式,对于每个点用单调队列维护凸壳即可。
时间复杂度$O(m\log m)$。
#include<cstdio> #include<algorithm> #include<vector> using namespace std; typedef long long ll; const int N=200010; const ll inf=1LL<<60; int n,m,i,e[N][4],cnt; struct E{ int x,y; E(){} E(int _x,int _y){x=_x,y=_y;} }w[N<<1]; ll dp[N],ans=inf; struct Line{ ll k,b; Line(){} Line(ll _k,ll _b){k=_k,b=_b;} ll f(ll x){return k*x+b;} }; vector<Line>g[N]; int head[N],tail[N],deg[N]; inline bool cmp(const E&a,const E&b){ if(a.x!=b.x)return a.x<b.x; return a.y<b.y; } inline double pos(const Line&a,const Line&b){ return 1.0*(a.b-b.b)/(b.k-a.k); } inline void ins(int o,ll k,ll b){ if(b>=inf)return; Line now(k,b); int&h=head[o],&t=tail[o]; if(h<=t){ if(g[o][t].k==k){ if(g[o][t].b<=b)return; t--; } } while(h<t&&pos(g[o][t-1],g[o][t])>=pos(g[o][t],now))t--; g[o][++t]=now; } inline ll cal(int o,ll x){ int&h=head[o],&t=tail[o]; if(h>t)return inf; while(h<t&&g[o][h].f(x)>g[o][h+1].f(x))h++; return g[o][h].f(x); } int main(){ scanf("%d%d",&n,&m); for(i=1;i<=m;i++){ scanf("%d%d%d%d",&e[i][0],&e[i][1],&e[i][2],&e[i][3]); deg[e[i][1]]++; w[++cnt]=E(e[i][2],-i);//s w[++cnt]=E(e[i][3],i);//e } sort(w+1,w+cnt+1,cmp); deg[1]++; for(i=1;i<=n;i++)tail[i]=-1,g[i].resize(deg[i]+2); ins(1,0,0);//kx+b for(i=1;i<=cnt;i++){ int x=w[i].y; //printf("->%d %d\n",w[i].x,x); if(x<0){ int y=-x; dp[y]=cal(e[y][0],e[y][2]); if(dp[y]<inf)dp[y]+=1LL*e[y][2]*e[y][2]; //printf("! %d %lld\n",y,dp[y]); }else{ //printf("ins %d %lld\n",e[x][1],dp[x]); ins(e[x][1],-2LL*e[x][3],dp[x]+1LL*e[x][3]*e[x][3]); } } for(i=1;i<=m;i++)if(e[i][1]==n)ans=min(ans,dp[i]); printf("%lld",ans); } /* 3 5 1 1 10 20 1 2 30 40 1 2 50 60 1 2 70 80 2 3 90 95 5 8 1 2 1 10 2 4 11 16 2 1 9 12 3 5 28 100 1 2 3 8 4 3 20 21 1 3 13 27 3 5 23 24 */
I. Long Long Strings
初始串取字符集无穷的超长字符串是最坏情况,压缩区间后暴力操作即可。
#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; template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; } int casenum, casei; int n; const LL inf = 1e12; struct A { LL st; LL ed; LL stval; bool operator < (const A &b)const { return st < b.st; } }; set<A>sot1, sot2; void doit(set<A> &sot) { char ch; while(~scanf(" %c", &ch)) { if(ch == 'D') { LL pos; scanf("%lld", &pos); auto it = --sot.upper_bound({pos}); LL st = it->st; LL ed = it->ed; LL stval = it->stval; sot.erase(it); vector<A>vt; if(st <= pos - 1) { vt.push_back({st, pos - 1, stval}); } if(pos + 1 <= ed) { vt.push_back({pos, ed - 1, stval + pos + 1 - st}); } for(auto w=sot.lower_bound({pos + 1}),nxt = w;w!= sot.end();w=nxt) { nxt = w; ++nxt; vt.push_back({w->st - 1, w->ed - 1, w->stval}); sot.erase(w); } for(auto it : vt)sot.insert(it); } else if(ch == 'I') { char val_; LL pos, val; scanf("%lld %c", &pos, &val_); val = inf + val_; auto it = --sot.upper_bound({pos}); LL st = it->st; LL ed = it->ed; LL stval = it->stval; sot.erase(it); vector<A>vt; if(st <= pos - 1) { vt.push_back({st, pos - 1, stval}); } if(pos <= ed) { vt.push_back({pos + 1, ed + 1, stval + pos - st}); } for(auto w=sot.lower_bound({pos + 1}),nxt = w;w!= sot.end();w=nxt) { nxt = w; ++nxt; vt.push_back({w->st + 1, w->ed + 1, w->stval}); sot.erase(w); } vt.push_back({pos, pos, val}); for(auto it : vt)sot.insert(it); } else break; /* for(auto it : sot) { printf("%lld %lld %lld\n", it.st, it.ed, it.stval); } puts("show end"); */ } } bool check() { LL ed1 = (--sot1.end())->ed; LL ed2 = (--sot1.end())->ed; if(ed1 != ed2)return 0; LL now = 1; while(now <= ed1) { auto it1 = sot1.begin(); auto it2 = sot2.begin(); LL ed1 = it1->ed; LL ed2 = it2->ed; if(it1->stval != it2->stval)return 0; LL stval = it1->stval; LL nxt = min(it1->ed, it2->ed) + 1; sot1.erase(it1); sot2.erase(it2); if(nxt <= ed1) { sot1.insert({nxt, ed1, nxt - now + stval}); } if(nxt <= ed2) { sot2.insert({nxt, ed2, nxt - now + stval}); } now = nxt; } return 1; } int main() { sot1.clear(); sot1.insert({1, inf, 1}); sot2.clear(); sot2.insert({1, inf, 1}); doit(sot1); doit(sot2); /* for(auto it : sot1) { printf("%lld %lld %lld\n", it.st, it.ed, it.stval); } puts("-------------"); for(auto it : sot2) { printf("%lld %lld %lld\n", it.st, it.ed, it.stval); } */ puts(check() ? "0" : "1"); return 0; } /* 【trick&&吐槽】 【题意】 【分析】 【时间复杂度&&优化】 */
J. Grid Coloring
设$f[i][j]$表示考虑前$i$行,第$i$行前$j$个都是蓝色,后面都是红色的方案数,暴力转移即可。
时间复杂度$O(n^3)$。
#include<cstdio> typedef long long ll; const int N=40; int n,m,i,j,k; ll ans,f[N][N]; char a[N][N]; bool can[N][N]; inline bool check(int x,int y){ for(int i=1;i<=y;i++)if(a[x][i]=='R')return 0; for(int i=y+1;i<=m;i++)if(a[x][i]=='B')return 0; return 1; } int main(){ scanf("%d%d",&n,&m); for(i=1;i<=n;i++)scanf("%s",a[i]+1); for(i=1;i<=n;i++){ for(j=0;j<=m;j++){ can[i][j]=check(i,j); } } //f[i][j] [1..j] is B, j+1..m is R for(j=0;j<=m;j++)f[1][j]=can[1][j]; for(i=1;i<n;i++)for(j=0;j<=m;j++)for(k=0;k<=j;k++)f[i+1][k]+=f[i][j]*can[i+1][k]; for(j=0;j<=m;j++)ans+=f[n][j]; printf("%lld",ans); }
K. Spinning Up Palindromes
显然最优解中一定是从右往左操作,故问题可以转化成:给定数字$A$,找到一个数位和最小的数字$B$,使得$A+B$是回文串。
考虑从两边往中间DP,设$f[i][j][k]$表示考虑前后$i$个位置,前面希望后面进位为$j$,后面对前面进位为$k$的最小代价。
#include<cstdio> #include<cstring> const int N=50,inf=10000000; int n,m,l,r,i,j,k,x,y,t,w; char s[N]; int a[N],f[N][2][2],ans=inf; inline void up(int&a,int b){a>b?(a=b):0;} int main(){ scanf("%s",s+1); n=strlen(s+1); for(i=1;i<=n;i++)a[i]=s[i]-'0'; for(i=0;i<=n+5;i++)for(j=0;j<2;j++)for(k=0;k<2;k++)f[i][j][k]=inf; f[0][0][0]=f[0][1][0]=0; for(l=1,r=n;l<r;l++,r--)for(i=0;i<2;i++)for(j=0;j<2;j++)if(f[l-1][i][j]<inf){ w=f[l-1][i][j]; for(x=0;x<10;x++)for(y=0;y<10;y++){//what is b for(k=0;k<2;k++){//jinwei from l+1 int A=a[l]+x+k,nA=0; if(A>=10)A-=10,nA++; if(nA!=i)continue; int B=a[r]+y+j,nB=0; if(B>=10)B-=10,nB++; if(A!=B)continue; up(f[l][k][nB],w+x+y); } } } m=n/2; if(n%2){ for(i=0;i<2;i++)for(j=0;j<2;j++)if(f[m][i][j]<inf){ w=f[m][i][j]; for(x=0;x<10;x++){ if((a[m+1]+x+j)/10!=i)continue; up(ans,w+x); } } }else{ for(i=0;i<2;i++)for(j=0;j<2;j++)if(f[m][i][j]<inf){ w=f[m][i][j]; if(i==j)up(ans,w); } } printf("%d",ans); }
L. Delayed Work
暴力枚举人数即可。
#include<cstdio> #include<algorithm> using namespace std; typedef long long ll; ll K,P,X,i;double ans; int main(){ scanf("%lld%lld%lld",&K,&P,&X); ans=1e20; for(i=1;i<=3000000;i++){ ans=min(ans,1.0*K*P/i+1.0*X*i); } printf("%.3f",ans); }
M. Unsatisfying
若初始2-SAT无解,那么答案显然为$0$。
如果不存在$\neg x\lor\neg y$,那么无解。
否则枚举每个$x$,强行规定$x=true$,若2-SAT无解则答案为$1$。
否则答案只能为$2$。
时间复杂度$O(n(n+m))$。
#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 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 = 4010, 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, nn; int x, y; vector<int> a[N]; bool pick[N]; int sta[N], top; bool dfs(int x) { if(pick[x ^ 1]) return 0; if(pick[x]) return 1; pick[x] = 1; sta[++ top] = x; for(int i = a[x].size() - 1; ~ i; -- i){ int y = a[x][i]; if(!dfs(y)) return 0; } return 1; } bool solve() { for(int i = 0; i < nn; i += 2) if(!pick[i] && !pick[i ^ 1]){ top = 0; if(!dfs(i)){ while(top) pick[sta[top --]] = 0; top = 0; if(!dfs(i ^ 1)) return 0; } } return 1; } int main() { scanf("%d%d", &n, &m); int flag = 0; nn = n * 2; for(int i = 0; i < nn; i ++) pick[i] = 0, a[i].clear(); for(int i = 1; i <= m; i ++){ scanf("%d%d", &x, &y); int xx = abs(x), yy = abs(y); xx *= 2; yy *= 2; xx --; yy --; if(x < 0 && y < 0) flag = 1; if(x < 0 && y < 0){ a[xx].push_back(yy ^ 1); a[yy].push_back(xx ^ 1); //printf("%d %d\n", xx, yy ^ 1); //printf("%d %d\n", yy, xx ^ 1); } else if(x < 0 && y > 0){ a[xx].push_back(yy); a[yy ^ 1].push_back(xx ^ 1); //printf("%d %d\n", xx, yy); //printf("%d %d\n", yy ^ 1, xx ^ 1); } else if(x > 0 && y < 0){ a[yy].push_back(xx); a[xx ^ 1].push_back(yy ^ 1); //printf("%d %d\n", yy, xx); //printf("%d %d\n", xx ^ 1, yy ^ 1); } else if(x > 0 && y > 0){ a[xx ^ 1].push_back(yy); a[yy ^ 1].push_back(xx); //printf("%d %d\n", xx ^ 1, yy); //printf("%d %d\n", yy ^ 1, xx); } } if(!flag){puts("-1"); return 0;} if(!solve()) {puts("0"); return 0;} for(int i = 1; i <= n; i ++){ for(int j = 0; j < nn; j ++){ //a[i].clear(); pick[j] = 0; } int xx = i * 2 - 1; //a[xx].push_back(xx ^ 1); a[xx ^ 1].push_back(xx); if(!solve()){ puts("1"); return 0; } //a[xx].pop_back(); a[xx ^ 1].pop_back(); } puts("2"); return 0; } /* 【trick&&吐槽】 【题意】 4 5 1 2 0 3 2 1 -1 -3 1 4 5 0 -2 3 3 5 4 2 3 -4 7 5 4 6 -2 -3 3 4 5 2 0 【分析】 【时间复杂度&&优化】 */