contest15
来源:互联网 发布:北塔软件规模 编辑:程序博客网 时间:2024/06/07 22:26
[比赛链接]https://vjudge.net/contest/168929#problem)
比赛征程
- Y : solved in the contest
- A : solved after contest
- SY : solving
比赛中解决的问题
A
Problem description
给你四个正整数a,b,c,d,问你存不存在时刻,使得(a+k1 * b)与 (c+k2 * s d)相等?k1,k2为正整数。
Data Limit:a,b,c,d<=100 Time Limit: 1s
Solution
1、直接暴枚到30000000 O(30000000)*小常数
2、枚举k1到10000 O(10000)*小常数
3、扩展欧几里德算法
Code
using namespace std;#include<cstdio>#include<iostream>#include<cstring>#include<algorithm>#include<cmath>#include<cstdlib>int x,y;int gcd(int x,int y){ return y==0?x:gcd(y,x%y);}void exgcd(int a,int b,int &x,int &y){ if(b==0){x=1;y=0;return;} exgcd(b,a%b,x,y);int xx=x,yy=y;y=(xx-(a/b)*yy);x=yy;}int a,b,c,d;int main(){ cin>>a>>b>>c>>d; int it=gcd(a,c); if(((d-b)/it)*it!=(d-b)){puts("-1");return 0;} exgcd(a,c,x,y); x*=(d-b)/it; y*=(d-b)/it;// cout<<x<<" "<<y<<endl; it=(a*c)/it; int t1=it/a,t2=it/c; while(y>0) { y-=t2;x+=t1; } while(abs(y)>t2) { y+=t2;x-=t1; } while(x<0) {x+=t1;y-=t2;} cout<<x*a+b<<endl;}
B
Problem description
每个宇宙来两个人,这两个人一叛徒一忠臣,他们报了一些组,当某一组全为叛徒时,机会取消,问是否有这样的可能性。
Data Limit:n <= 1e4 Time Limit: 2s
Solution
直接模拟即可。每次每组若出现两个来自同一个平行宇宙的,说明该组没危险。
Code
scanf("%d",&n);cin>>m; for(int i=1;i<=m;i++) { scanf("%d",&num); memset(vis,0,sizeof(vis)); bool flag=0; for(int j=1;j<=num;j++) { scanf("%d",&pp[j]); } for(int j=1;j<=num;j++) if(pp[j]>0) vis[pp[j]]=1; for(int j=1;j<=num;j++) if(pp[j]<0) if(vis[-pp[j]]){flag=1;break;} if(flag);else {puts("YES");return 0;} } puts("NO");
赛后补题
C
Problem description
两个人玩游戏,每个人有一个集合,两人轮流游戏,每次他们能让怪兽顺时针走他们选出的自己集合中的数步(可以多次使用),某人走完后怪兽到了结束点就赢了,问对于怪兽在每个位置(不在
游戏结束点)某人先手是否必胜/必输或有策略无限循环?
Data Limit:n <= 7e3 Time Limit: 4s
Solution
这种博弈问题看到题目就两种思路:
1、SG函数,这道题很难用
2、广搜的赢点输点DP,一方不能再走为输
对于一个赢点,那么所有对方能转移到他的点都为输点。
对于一个点,如果所有他能够出发到达的点都是对手的赢点,那么该点为输点
这是一大类套路DP,我要多做这方面的题。
Code
using namespace std;#include<cstdio>#include<iostream>#include<cstring>#include<algorithm>#include<queue>#include<map>#define N 7001struct NODE{ int x,y,z;}ind;map<int,string>M;int k[2],s[N][2];int n,m;bool b[N][3];int ans[N][3];int du[N][3];queue<NODE>q;void p(int x,int y,int z){ if(b[x][y]) return; ind.x=x;ind.y=y;ind.z=z;// cout<<x<<" "<<y<<" "<<z<<endl; b[x][y]=1;q.push(ind);ans[x][y]=z;}int main(){ scanf("%d",&n); for(int i=0;i<=n;i++) for(int j=0;j<2;j++) ans[i][j]=3; for(int i=0;i<2;i++) { scanf("%d",&k[i]); for(int j=1;j<=k[i];j++) scanf("%d",&s[j][i]); } for(int i=0;i<2;i++) { for(int j=1;j<=n;j++) { du[j][i]=k[i]; } } M[0]="Win";M[1]="Lose";M[3]="Loop"; p(0,0,1);p(0,1,1); while(!q.empty()) { NODE hh=q.front();q.pop(); int x=hh.x,y=hh.y,z=hh.z; if(z) for(int i=1;i<=k[y^1];i++) p((x-s[i][y^1]+n*5)%n,y^1,0); else { for(int i=1;i<=k[y^1];i++) { du[(x-s[i][y^1]+n*5)%n][y^1]--; if(!du[(x-s[i][y^1]+n*5)%n][y^1]) p((x-s[i][y^1]+n*5)%n,y^1,1); } } } for(int i=0;i<2;i++) { for(int j=1;j<=n-1;j++) { cout<<M[ans[j][i]]<<" "; } puts(""); }}
D
Problem description
有三种类型的道路,1.花费x,从u->v.
2、花费x,从u->[L,R]中任意一点。
3、花费x,从[L,R]中任意一点->u;
求s,到所有点的最少花费
Data Limit:n <= 1e5 Time Limit: 2s
Solution
这题是虚拟建建节点的方法,利用类似线段树的构造,把边数优化到nlogn
这种题用单项边汇。
Code
using namespace std;#include<cstdio>#include<iostream>#include<algorithm>#include<cstring>#include<queue>#define r(x) scanf("%lld",&x)#define forw(i,x) for(long long i=fir[x];i;i=ne[i])#define N 3000001long long root1,n,m,root2;long long ty,x,u,v,l,r,w;long long ne[N],dis[N],fir[N],to[N],C[N],cnt,tot;bool inq[N];long long s;void add(long long x,long long y,long long z){ ne[++cnt]=fir[x];fir[x]=cnt;to[cnt]=y;C[cnt]=z;}long long L[N],R[N];long long lc[N],rc[N],sz;queue<long long>q;long long build1(long long l,long long r){ if(l==r) {L[l]=l;R[l]=r;return l;} ++sz; long long mid=(l+r)>>1; long long no=sz; add(no,lc[no]=build1(l,mid),0);add(no,rc[no]=build1(mid+1,r),0); L[no]=l;R[no]=r; return no;}long long build2(long long l,long long r){ if(l==r) {L[l]=l;R[l]=r;return l;} ++sz;long long mid=(l+r)>>1; long long no=sz; add(lc[no]=build2(l,mid),no,0);add(rc[no]=build2(mid+1,r),no,0); L[no]=l;R[no]=r; return no;}void ADD1(long long no,long long u,long long l,long long r,long long w){ if(L[no]==l&&R[no]==r) {add(u,no,w);return;} long long mid=(L[no]+R[no])>>1; if(l>mid) ADD1(rc[no],u,l,r,w);else if(r<=mid) ADD1(lc[no],u,l,r,w);else { ADD1(lc[no],u,l,mid,w); ADD1(rc[no],u,mid+1,r,w); return; }}void ADD2(long long no,long long u,long long l,long long r,long long w){ if(L[no]==l&&R[no]==r) {add(no,u,w);return;} long long mid=(L[no]+R[no])>>1; if(l>mid) ADD2(rc[no],u,l,r,w);else if(r<=mid) ADD2(lc[no],u,l,r,w);else { ADD2(lc[no],u,l,mid,w); ADD2(rc[no],u,mid+1,r,w); return; }}void SPFA(long long s){ q.push(s);inq[s]=1;dis[s]=0; while(!q.empty()) { long long ind=q.front();q.pop();inq[ind]=0; forw(i,ind) { long long V=to[i]; if(dis[V]>dis[ind]+C[i]) { dis[V]=dis[ind]+C[i]; if(!inq[V]) { inq[V]=1; q.push(V); } } } }}main(){ cin>>n>>m>>s;sz=n; root1=build1(1,n); root2=build2(1,n); for(long long i=1;i<=m;i++) { cin>>ty; if(ty==1) { r(u);r(v);r(w);add(u,v,w); } if(ty==2) { r(v);r(l);r(r);r(w); ADD1(root1,v,l,r,w); } if(ty==3) { r(v);r(l);r(r);r(w); ADD2(root2,v,l,r,w); } } for(long long i=1;i<=sz;i++) dis[i]=1e18; SPFA(s); for(long long i=1;i<=n;i++) { if(dis[i]<1e18) cout<<dis[i]<<' ';else cout<<"-1 "; }}
E
Problem description
一个有n个颜色的排列,对于特定的k,求将原序列分成最少的连续的段数,使每段的颜色数小于等于k。输出k=1~n的答案。
Data Limit:n <= 1e5 Time Limit: 2s
Solution
1、如果k=l的个数等于k=r的个数那么k=l~r这么多数答案均相同,这样就每次二分+模拟就可以了。调和级数约为logn.所以O(nlog^2n)
2、这类区间的题,可以用切割区间然后按左端点排序从左到右查找更新的方法解决,用树状数组维护从i~j内的不同颜色数,然后二分最远位置即可。
特殊处理就是nxt数组记录下一个这种颜色,以便查询完i后删除i。很好的方法,对离线问题值得借鉴!
##Code1
using namespace std;#include<cstdio>#include<iostream>#include<cstring>#include<algorithm>#define N 200001int n,color[100001];int ans[N];int confuse[100001];bool num[100001];int cal(int x){ if(confuse[x]) return confuse[x]; for(int l=1;l<=n;) { int cnt=0; int f=l; while(cnt<x&&f<=n) { if(!num[color[f]])cnt++; num[color[f++]]=1; } while(num[color[f]]&&f<=n)f++; if(!num[color[f]])f--; for(int k=l;k<=f;k++) num[color[k]]=0; confuse[x]++;l=f+1; } return confuse[x];}void doit(int l,int r){ if(l>r) return; int rr=r,ll=l+1; int an=l; while(ll<=rr) { int mid=(ll+rr)>>1; if(cal(mid)==cal(l)) { ll=mid+1;an=mid; } else rr=mid-1; } for(int i=l;i<=an;i++) { ans[i]=cal(l); } doit(an+1,r);}int main(){ scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&color[i]); doit(1,n); for(int i=1;i<=n;i++) { cout<<ans[i]<<" "; }}
##Code1
using namespace std;#include<cstdio>#include<iostream>#include<cstring>#include<algorithm>#include<vector>#define N 300001#define F(i,a,b) for(int i=a;i<=b;i++)#define lowbit(x) (x&(-x))int c[N],S[N];int n,m;vector<int>q[N];int col[N];void add(int x,int v){ for(int i=x;i<=n;i+=lowbit(i)) S[i]+=v;}int get(int k){ int res=0; for(int i=16;i>=0;i--) { int x=(1<<i); if(res+x<=n) if(S[res+x]<=k) { res+=x; k-=S[res]; } } return res;}int num[N];int ans[N];int nxt[N];int main(){ scanf("%d",&n);for(int i=1;i<=n;i++) scanf("%d",&c[i]); for(int i=1;i<=n;i++) { q[1].push_back(i); if(!num[c[i]]) { num[c[i]]=i; add(i,1); } else { nxt[num[c[i]]]=i; num[c[i]]=i; } } F(i,1,n) { int hh=q[i].size(); for(int j=0;j<hh;j++) { int xx=get(q[i][j]); ans[q[i][j]]++; q[xx+1].push_back(q[i][j]); } hh=nxt[i]; add(i,-1);if(hh>0) add(hh,1); } for(int i=1;i<=n;i++) cout<<ans[i]<<" ";}