Codeforces Round #532 (Div. 2)
题意:给出k,让你求b,使所有位置c(c满足c = b + i * k) 的标签全部消失,使1和-1的数量相差最大,求b。
枚举。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=100010; int n,k; int aa,bb; int p; int v[110]; int main(){ while(cin>>n>>k) { p=1000*k; for(int i=1;i<=n;i++) { int u; cin>>v[i]; if(v[i]>0)aa++; else bb++; } int ans=0; for(int b=1;b<=n;b++) { int xx=aa,yy=bb; for(int j=b-1;j>0;j--) { if((b-j)%k==0) { if(v[j]>0)xx--; else yy--; } } for(int j=b;j<=n;j++) { if((j-b)%k==0) { if(v[j]>0)xx--; else yy--; } } ans=max(ans,abs(xx-yy)); } cout<<ans<<endl; } }
题意:依次给出数字,当凑足了1-n时输出1,否则输出0。
记录当前位置有几个数字了,凑满数字就所有vis(次数数组减一),一开始居然想到用线段树维护维护这个减一操作和查询次数,因为觉得n太大了每次都扫一遍会超时。后来发现,由于m时固定的,所以扫描n的次数最多是m/n次,每次要n的时间,所以就是o(m)的时间。所以暴力不会出事。
#include<bits/stdc++.h> #define CLR(a,b) memset(a,b,sizeof(a)) using namespace std; typedef long long ll; const int maxn=100010; int n,m; int a[maxn]; int vis[maxn]; string ans; int main(){ while(cin>>n>>m) { CLR(vis,0); ans=""; for(int i=1;i<=m;i++) { scanf("%d",&a[i]); } int tot=0; for(int i=1;i<=m;i++) { if(vis[a[i]]==0){ tot++; vis[a[i]]=1; }else{ vis[a[i]]++; } if(tot<n)ans+="0"; else{ ans+="1"; tot=0; for(int j=1;j<=n;j++) { vis[j]--; if(vis[j]>0)tot++; } } } cout<<ans<<endl; } }
C. NN and the Optical Illusion
初中计算几何题,设外半径为R,半径为r,可以列方程,会解二元一次方程就可以,直接套求根公式。
#include<bits/stdc++.h> #define CLR(a,b) memset(a,b,sizeof(a)) using namespace std; typedef long long ll; const int maxn=100010; const double pai=acos(-1.0); double n,r,R,p,die; int main(){ while(cin>>n>>r) { p=cos(2*pai/n); // cout<<p<<endl; die=(4*r*p-4*r)*(4*r*p-4*r)-4*(2+2*p)*(2*r*r*p-2*r*r); double ans=((4*r-4*r*p)+sqrt(die))/(4+4*p); printf("%.8f\n",ans); } }
交互题,学习了博客,题意好像又搞错了,看了博客后,代码就跟默写一样了……交互题还是不太会啊。
就是要记住,方向数组的变化, i 和 j 对应的是y 和 x,不要弄反了。
转一位大佬的博客,讲的很清晰。
#include<bits/stdc++.h> #define clr(a,b) memset(a,b,sizeof(a)) using namespace std; typedef long long ll; const int maxn=1e3+10; int fx[4][2]={{1,1},{1,-1},{-1,1},{-1,-1}}; int vis[maxn][maxn],bx[680],by[680],u,px,py,x,y; int s1[10],s2[10]; void Move(int xx,int yy){ x+=xx; y+=yy; if(vis[x][y])x-=xx; printf("%d %d\n",x,y); fflush(stdout); scanf("%d%d%d",&u,&px,&py); if(u==-1&&px==-1&&py==-1)exit(0); vis[bx[u]][by[u]]=0; vis[px][py]=1; bx[u]=px; by[u]=py; } int main(){ cin>>x>>y; for(int i=1;i<=666;i++) { scanf("%d%d",&bx[i],&by[i]); vis[bx[i]][by[i]]=1; } while(x<500)Move(1,0); while(y<500)Move(0,1); while(x>500)Move(-1,0); while(y>500)Move(0,-1); for(int i=1;i<=499;i++) for(int j=1;j<=499;j++) if(vis[i][j]) s1[1]++; for(int i=1;i<=499;i++) for(int j=501;j<=999;j++) if(vis[i][j]) s1[2]++; for(int i=501;i<=999;i++) for(int j=1;j<=499;j++) if(vis[i][j]) s1[3]++; for(int i=501;i<=999;i++) for(int j=501;j<=999;j++) if(vis[i][j]) s1[4]++; s2[1]=s1[2]+s1[3]+s1[4]; s2[2]=s1[1]+s1[3]+s1[4]; s2[3]=s1[1]+s1[2]+s1[4]; s2[4]=s1[1]+s1[2]+s1[3]; int maxx=0,tep; for(int i=1;i<=4;i++) { if(s2[i]>maxx){ maxx=s2[i]; tep=i-1; } } while(true) { Move(fx[tep][0],fx[tep][1]); } }
题意:给出一副有向图,每条边的权值是代表使这条边反向所需要的能力,现在希望使整幅图无环,并且只要使一条边反向了,其他所有权值小于等于这条边的边都可以免费反向。
题解:显然是一个可以二分的题目,因为对所有边进行合法操作必然能得到一个合法的图,这个limit越小则限制越严格。
对于一个二分出来的权值k,我们可以先把所有权值小于等于k的边全部取消,而其他边全部变成拓扑偏序关系,由于取消掉了这些边,所以这些边的影响相当于是最后由我决定的,只要我能写出合法的拓扑序列,那我按照这个拓扑序列来决定边的方向即可。合法的拓扑序列即拓扑完成后元素个数等于n(否则说明有环,元素还在队列里)。
不断二分k,那怎么根据拓扑序列来决定边的方向呢?其实就是看每个元素的拓扑序号是多少,比如u和v,如果u有一条连向v的有向边,但u的拓扑序号却比v的拓扑序号大,说明在拓扑序列中,u在v的后面,所以这条边要被反向,即输出。
#include<bits/stdc++.h> #define clr(a,b) memset(a,b,sizeof(a)) using namespace std; typedef long long ll; const int maxn=1e5+10; int n,m; struct node{ int u,v; ll w; }; struct edge{ int to,Next; }e[maxn]; vector<node >a; int u,v,tot,head[maxn],d[maxn],top[maxn]; ll w,l,r,mid; void addv(int u,int v){ e[++tot].to=v; e[tot].Next=head[u]; head[u]=tot; } bool toposort(){ queue<int >q; int cnt=0; for(int i=1;i<=n;i++) { if(d[i]==0){ q.push(i); } } while(!q.empty()) { int u=q.front(); top[u]=++cnt; q.pop(); for(int i=head[u];i!=-1;i=e[i].Next) { int v=e[i].to; d[v]--; if(d[v]==0) { q.push(v); } } } if(cnt==n)return true; return false; } void buildGraph(ll mid){ clr(head,-1),tot=0; clr(d,0); for(int i=0;i<m;i++) { int st=a[i].u,ed=a[i].v; ll val=a[i].w; if(val>mid){ addv(st,ed); d[ed]++; } } } vector<int >ans; int main(){ while(cin>>n>>m) { a.clear(); ans.clear(); l=0,r=0; for(int i=0;i<m;i++) { scanf("%d%d%lld",&u,&v,&w); a.push_back({u,v,w}); r=max(r,w); } while(l<r) { mid=(l+r)>>1; buildGraph(mid); if(toposort()){ r=mid; }else { l=mid+1; } } buildGraph(r); toposort(); for(int i=0;i<m;i++) { if(a[i].w<=r && top[a[i].u]>top[a[i].v])ans.push_back(i+1); } int si=ans.size(); printf("%lld %d\n",r,si); for(int i=0;i<si;i++) { printf("%d%c",ans[i]," \n"[i==si-1]); } } }
给出 n 个数,q次区间查询,每次查询,让你选择任意个下标为 [ l , r ] 区间内的任意数,使这些数异或起来最大,输出最大值。
思路:离线加线性基。
对于此题,先把区间按照 r 从小到大排序,然后依次处理这些区间,每次插入线性基时,优先保留下标比较大的线性基。查询时,只异或上下标大于 l 的值。
记住异或的符号的优先级很低,所以 if( res^p[i] > res )这样的代码是会wa死的,要注意(这道题这么写,样例都过不了)
#include<bits/stdc++.h> #define clr(a,b) memset(a,b,sizeof(a)) using namespace std; typedef long long ll; const int maxn=5e5+10; int a[maxn],q,n,p[30],pos[30],ans[maxn]; struct node{ int l,r,id; friend bool operator<(const node &a,const node &b) { return a.r<b.r; } }op[maxn]; void init(){ clr(p,0); } void add(int val,int id){ for(int i=20;i>=0;i--) { if(val&(1<<i)) { if(!p[i]){ p[i]=val,pos[i]=id; break; } if(pos[i]<id){ swap(pos[i],id),swap(val,p[i]); } val^=p[i]; } } } int query(int l) { int res=0; for(int i=20;i>=0;i--) { if(pos[i]>=l) { if((res^p[i])>res) { res=res^p[i]; } } } return res; } int main(){ while(cin>>n) { init(); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); } cin>>q; for(int i=1;i<=q;i++) { scanf("%d%d",&op[i].l,&op[i].r); op[i].id=i; } sort(op+1,op+1+q); int l=1; for(int i=1;i<=q;i++) { while(l<=op[i].r&&l<=n) { add(a[l],l); l++; } ans[op[i].id]=query(op[i].l); } for(int i=1;i<=q;i++) { printf("%d\n",ans[i]); } } }