Gym101986: Asia Tsukuba Regional Contest(寒假自训第12场)
A .Secret of Chocolate Poles
题意:有黑白两种木块,黑色有1,K两种长度; 白色只有1一种长度,问满足黑白黑...白黑形式,长度为L的组合种类。
思路:直接DP即可。
#include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=200010; ll dp[110][2],ans; int main() { int L,K; scanf("%d%d",&L,&K); dp[0][0]=1; rep(i,1,L){ dp[i][0]=dp[i-1][1]; dp[i][1]=dp[i-1][0]; if(i-K>=0) dp[i][1]+=dp[i-K][0]; } rep(i,1,L) ans+=dp[i][1]; printf("%lld\n",ans); return 0; }
B .Parallel Lines
题意:给定二维平面上N个点,N是偶数,限制让你两两连线,使得平行线对数最多。N<=16;
思路:N为16时,复杂度=15*13*11*9*7*5*3*1~=2e6;所以可以枚举所有情况,即搜索可以做。 注意是两两配对,没有不选的情况,不然复杂度很高。
统计时用了map带了个log。 这个log还可以优化掉,比如,变为每次加入一个斜率x时,res+=num[x]; num[x]++; 就不用map记录每个斜率的个数,最后才去统计答案了。
#include<bits/stdc++.h> #define ll long long #define pii pair<int,int> #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=20; int X[maxn],Y[maxn],ans,N,vis[maxn]; map<pii,int>mp; map<pii,int>::iterator it; pii a[maxn][maxn]; void dfs(int pos) { if(pos==N+1){ int res=0; for(it=mp.begin();it!=mp.end();it++){ res+=(it->second)*((it->second)-1)/2; } ans=max(ans,res); return ; } if(!vis[pos]) for(int i=pos+1;i<=N;i++){ if(!vis[i]){ vis[i]=1; mp[a[pos][i]]++; dfs(pos+1); mp[a[pos][i]]--; vis[i]=0; } } else dfs(pos+1); } int main() { scanf("%d",&N); rep(i,1,N) scanf("%d%d",&X[i],&Y[i]); rep(i,1,N) rep(j,i+1,N){ int dx=X[j]-X[i],dy=Y[j]-Y[i]; if(dx<0) dx=-dx,dy=-dy; if(dx==0) dy=abs(dy); int g=__gcd(dx,dy); dx/=g; dy/=g; a[i][j]=pii(dx,dy); } dfs(1); printf("%d\n",ans); return 0; }
C .Medical Checkup
题意:有有无限个医院,N个学生,第i个学生在每个医院检查的时间都一样,是hi。 他们排成一队去检查。 满足,一个医院不能同时检查多个人,一个人前面的那个人检查完后才能去检查, 给定时间T,问每个人在哪个医院里检查(或者在等候)。
思路:对思维题不感冒啊,想的过于复杂了。 这个题,对于所有的人都满足的东西是开始入队的时间是一定的,即h1+h2+...hi-1。
然后,从他开始检查开始,就把入队的看成一个整体,那么,这个整体的队尾前进的距离,取决于整个队伍前进的最小距离。
所以不难得出他们的答案=(T-h1-h2-...)/hi+2;
#include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=200010; ll h[maxn]; int main() { int N; ll sum=0,T,Mx=0; scanf("%d%lld",&N,&T); rep(i,1,N){ scanf("%lld",&h[i]); Mx=max(Mx,h[i]); sum+=h[i]; if(T>=sum) printf("%lld\n",(T-sum )/Mx+2); else printf("1\n"); } return 0; }
E .Black or White
题意:给定一排,N个砖,只有黑白两色,给出它们初始的颜色S,限制你有长度为1到K的刷子,问至少刷多少次,可以把它们刷成给定状态T。
思路:假设一排长度为M的没有颜色砖,染色成BWBWBW..的形式,至少需要(M+1)/2+1刷子。 所以其实初始状态没有多大用处(除非相同),我们只考虑目标状态。
我们把T种相邻颜色相同的看成一个整体,那么:
dp[i]=dp[i-1]; 当s[i]==t[i];
dp[i]=dp[j]+(sum[i]-sum[j+1]+1)/2+1; 当s[i]!=t[i];
第二个方程直接单调队列可以搞。 如果我们考虑了初始状态,那么只会越想越复杂。
#include<bits/stdc++.h> #define rep(i,w,v) for(int i=w;i<=v;i++) using namespace std; const int maxn=500001; int dp[maxn],sum[maxn],q[maxn],head,tail,N,K; char a[maxn],b[maxn]; int main() { scanf("%d%d",&N,&K); scanf("%s%s",a+1,b+1); rep(i,1,N) sum[i]=sum[i-1]+(b[i]!=b[i-1]); head=tail=1; q[1]=0; rep(i,1,N){ if(a[i]==b[i]) dp[i]=dp[i-1]; else dp[i]=dp[q[head]]+(sum[i]-sum[q[head]+1]+1)/2+1; while(tail>=head&&dp[i]*2-sum[i+1]<=dp[q[tail]]*2-sum[q[tail]+1]) tail--; while(tail>=head&&i-q[head]>=K) head++; q[++tail]=i; } printf("%d\n",dp[N]); return 0; }
I .Starting a Scenic Railroad Service
题意:给定N个人的陆游线路,问自己选票的情况下,和售票方选票的情况下,旅游车需要多少个位置。
思路:反正题意差不多是这样,第一个问是问max(一个人的路线+和他规范相交的路线个数)。 第二个问是max(单点最大路线覆盖);
第二个问,由于数据小,可以用差分或者线段树; 如果数据大,可以直接排序做。 是个常规题了。
第一个问,和一个线段相交的线段个数,=N-在他前面的-在他后面的。 不难想到也可以差分去做。 数据大后还是可以排序来做。
覆盖问题,个人更喜欢写排序,因为不用考虑下标大小,或者离散化导致的边界问题。
#include<bits/stdc++.h> #define ll long long #define pii pair<int,int> #define f first #define s second #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=1000000; pii a[maxn],b[maxn]; int A[maxn],B[maxn],ans1,ans2,tot; int main() { int N,pre=0; scanf("%d",&N); rep(i,1,N) { scanf("%d%d",&a[i].f,&a[i].s); b[++tot]=pii(a[i].f,1); b[++tot]=pii(a[i].s,-1); } rep(i,1,N){ A[a[i].f]++; B[a[i].s]++; } rep(i,1,100000) A[i]+=A[i-1],B[i]+=B[i-1]; rep(i,1,N){ int f=B[a[i].f]+N-A[a[i].s-1]; ans1=max(ans1,N-f); } sort(b+1,b+tot+1); rep(i,1,tot){ if(b[i].s==-1) pre--; else pre++; ans2=max(ans2,pre); } printf("%d %d\n",ans1,ans2); return 0; }
F .Pizza Delivery
题意:给定N点M边有向带权图,起点S=1,终点T=2; 对于每个边,问他转向后,起点到终点的最短路变大还是变小或者不变。
思路:首先肯定要两次SPFA,求出S和T到每个点的最短路,分别是dis1,dis2;
当且仅当,然后如果dis1[v]+dis2[u]+w[i]<dis1[T],那么这条边可以让最短路变小。
否则,最短路变大或者不变,如果不变,说明最短路不是必经这条边。
那么本题的关键问题就是问边是否是最短路必经边。
我们把最短路上的边抽出来(就是满足dis1[u]+dis2[v]+w[i]==dis1[T]那些有向边,把它看成无向边),然后不难想到就是求“桥”。
如果桥转向了,那么最短路一定变长。
#include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=200010; const ll inf=1LL<<50; struct dijst { struct in{ ll dis;int u; in(){} in(ll d,int uu):dis(d),u(uu){} bool friend operator<(in w,in v){ return w.dis>v.dis; } }; ll dis[maxn]; int Len[maxn],cnt,S; int Laxt[maxn],Next[maxn],inq[maxn],To[maxn]; void init(int N){ cnt=0; rep(i,1,N) inq[i]=Laxt[i]=0,dis[i]=inf; } void add(int u,int v,int w){ Next[++cnt]=Laxt[u]; Laxt[u]=cnt; To[cnt]=v; Len[cnt]=w; } void SPFA() { dis[S]=0; priority_queue<in>q; q.push(in(0LL,S)); while(!q.empty()) { int u=q.top().u; q.pop(); inq[u]=0; for(int i=Laxt[u];i;i=Next[i]){ int v=To[i]; if(dis[v]>dis[u]+Len[i]){ dis[v]=dis[u]+Len[i]; if(!inq[v]) inq[v]=1,q.push(in(dis[v],v)); } } } } }D1,D2; int a[maxn],b[maxn],c[maxn],cnt; int dfn[maxn],low[maxn],times,Br[maxn]; int Laxt[maxn],Next[maxn],To[maxn],id[maxn]; void add(int u,int v,int p) { Next[++cnt]=Laxt[u]; Laxt[u]=cnt; To[cnt]=v; id[cnt]=p; } void tarjan(int u,int fa) { dfn[u]=low[u]=++times; for(int i=Laxt[u];i;i=Next[i]){ int v=To[i]; if(!dfn[v]){ tarjan(v,u); low[u]=min(low[u],low[v]); if(low[v]>dfn[u]) Br[id[i]]=1; } else if(v!=fa) low[u]=min(low[u],dfn[v]); } } int main() { int N,M; scanf("%d%d",&N,&M); D1.init(N); D2.init(N); D1.S=1; D2.S=2; rep(i,1,M) { scanf("%d%d%d",&a[i],&b[i],&c[i]); D1.add(a[i],b[i],c[i]); D2.add(b[i],a[i],c[i]); } D1.SPFA(); D2.SPFA(); ll Dis=D1.dis[2]; rep(i,1,M){ if(D1.dis[a[i]]+D2.dis[b[i]]+c[i]==Dis){ add(a[i],b[i],i); add(b[i],a[i],i); } } rep(i,1,N) if(!dfn[i]) tarjan(i,0); rep(i,1,M){ if(D1.dis[b[i]]+D2.dis[a[i]]+c[i]<Dis) printf("HAPPY\n"); else if(Br[i]) printf("SAD\n"); else printf("SOSO\n"); } return 0; }