【一堆题】from wxh 学长的一堆题(流量党慎入)
见到了久闻帅气的曾给我发X网的wxh学长,一堆一堆很杂的题(更新结束)
from wxh学长
23333333(小孩子不要打开看哦(滑稽))
T1:GYM100738A
题意:给两个矩形长宽判断一个矩形可不可以在另一个矩形内
考虑a,b,c,d,矩形长宽换位置共8种情况(很重要) 我们设旋p角,利用c得到p角(解三角形)后,判断是否asinp+bcosp是否小于d,就可以了
一道带权并查集题,暂时不想写,思路特别简单:记录带权并查集表示他与其父亲的异或值,有路径压缩,直接将第二个看做并查集连边,如果fa[fx]=fy,那么w[fx]=w[x]^w[y]^c就可以了,第一个看做向虚拟结点n+1连边,第三个在各个并查集里面找,如果找到某个并查集为奇数就搞不出来i don't know,如果找到为偶数,直接全部搞异或就可以了。
类似于可持久化的思想,如果1,2,3操作我们都当做i向i-1连边,第4个操作我们将其与x连边,那么我们从初始操作跑一次dfs(每次回溯时将更改抹平)就可以了,维护矩阵暴力维护。


#include<cstdio> #include<algorithm> #include<cmath> #include<iostream> #include<cstdlib> using namespace std; bool check(double a,double b,double c,double d) { if(a*b>c*d) return false; if(a<=c&&b<=d) return true; double dd = sqrt(a*a+b*b); double p = asin(c/dd); double q = asin(a/dd); double jiao = p-q; if(jiao<0) return 0; if(a*sin(jiao)+b*cos(jiao)<=d) return true; return false; } int main() { double a,b,c,d; scanf("%lf%lf%lf%lf",&a,&b,&c,&d); if(check(c,d,a,b)||check(d,c,a,b)||check(c,d,b,a)||check(d,c,b,a)||check(a,b,c,d)||check(b,a,c,d)||check(a,b,d,c)||check(b,a,d,c)) printf("Yes"); else printf("No"); }GYM100283A 题意:给n条边看能不能组成凸n边形,并找出最小圆形覆盖半径 一问直接看n-1条短边之和是否大于第三边 二问二分答案半径长度,然后根据圆心角之和是否大于360度判断,记得要用acos(-1)不然莫名被卡orz
#include<cstdio> #include<iostream> #include<cmath> #include<algorithm> using namespace std; double pi = acos(-1); int t; int n; int a[2005]; bool isok(double r) { double sum = 0; for(int i=1;i<=n;i++) { if(a[i]>r*2) return 0; sum+= asin(a[i]/2.0/r); } if(sum<=pi) return 1; else return 0; } int main() { freopen("zeriba.in","r",stdin); scanf("%d",&t); for(int i=1;i<=t;i++) { printf("Case %d: ",i); scanf("%d",&n); int maxx = 0; int sum = 0; for(int j=1;j<=n;j++) { scanf("%d",&a[j]); if(a[j]>a[maxx]) maxx = j; sum+=a[j]; } sum-=a[maxx]; // isok(3); if(sum<=a[maxx]) printf("can't form a convex polygon\n"); else { double l = 0 ; double r = 3000000; double mid; while(r-l>=0.000001) { mid = (l+r)*0.5; if(isok(mid)) r=mid; else l = mid; } printf("%.4f\n",r); } } }T3:div2 365d 大致题意:给定一个序列,求这个区间内出现偶数次的数的异或和 一段区间内的出现偶数次异或和为这段区间的异或和异或上这段区间所有种类数(每种数出现一次)的异或和,那么我们类似HH的项链离线+树状数组+前缀异或维护即可
#include<cstdio> #include<iostream> #include<algorithm> #define lowbit(x) ((x)&(-x)) using namespace std; const int maxn = 1000005; int n,m; int c[maxn]; int getsum(int x) { int sum=0; for(;x;x-=lowbit(x)) sum^=c[x]; return sum; }; void modify(int x,int d) { for(;x<=n+2;x+=lowbit(x)) c[x]^=d; return; } int haha[maxn]; int aa[maxn]; int la[maxn],nt[maxn]; int fi(int x) { return lower_bound(haha+1,haha+1+n,x)-haha; } int ans[maxn]; struct node { int l,r,id; }z[maxn]; bool cmp(node aa,node bb) { return aa.l<bb.l; } int sum[maxn]; int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&haha[i]); aa[i]=haha[i]; sum[i] = sum[i-1]^aa[i]; } sort(haha+1,haha+1+n); for(int i=n;i>=1;i--) { aa[i] = fi(aa[i]); int x = aa[i]; if(la[x]) nt[i] = la[x]; else nt[i] = n+1; la[x] = i; } for(int i=1;i<=n;i++) if(la[i]) modify(la[i],haha[i]); scanf("%d",&m); for(int i=1;i<=m;i++) { scanf("%d%d",&z[i].l,&z[i].r); z[i].id = i; } sort(z+1,z+1+m,cmp); int L=1; for(int i=1;i<=m;i++) { while(L<z[i].l) modify(nt[L],haha[aa[L]]),L++; ans[z[i].id] = sum[z[i].r]^sum[z[i].l-1]^(getsum(z[i].r)^getsum(z[i].l-1)); } for(int i=1;i<=m;i++) { printf("%d\n",ans[i]); } }T4: GYM100739A 题意:和GDKOI魔卡少女相同,带修改+查询区间内所有子区间的异或和的总和 数据范围只有1000,可以考虑拆位(异或位数间互不影响),问题转化求区间异或和为1区间个数,对于每一个位置建立一棵线段树,维护线段树:L[0/1]以左端点开头的异或和为0/1区间个数,R[0/1]以右端点开头异或和为0/1区间个数(前后缀)和区间异或的总和。答案每次合并时的答案=左的答案+右的答案+Lson.R[1]*Rson.L[0]+Lson.R[0]*Rson.L[1]。
#include<cstdio> #include<iostream> #include<cmath> using namespace std; const int maxn = 100005; const int segmaxn = 3000005; const int maxcnt = 10; const int mod = 4001; struct node { int ls,rs,L[2],R[2],ans,d; } z[segmaxn] ; int n,m; int bit[maxn]; int aaa[maxn]; int aacnt[maxn][13]; int owo,rt[maxn]; void cle(int p) { z[p].ls=z[p].rs=z[p].L[1]=z[p].L[0]=z[p].R[0]=z[p].R[1]=z[p].ans=z[p].d=0; } node merge(int lc,int rc) { node ll = z[lc]; node rr=z[rc]; node xx=(node){ lc,rc,0,0,0,0,ll.ans+rr.ans,ll.d^rr.d }; int dl = ll.d; int dr = rr.d; xx.L[0] = ll.L[0] + rr.L[dl?1:0]; xx.L[1] = ll.L[1] + rr.L[dl?0:1]; xx.R[0] = rr.R[0] + ll.R[dr?1:0]; xx.R[1] = rr.R[1] + ll.R[dr?0:1]; xx.ans=(xx.ans+(ll.R[1]*rr.L[0] )%mod+ (ll.R[0]*rr.L[1])%mod)%mod; return xx; } node mermer(node ll,node rr) { node xx=(node){ 0,0,0,0,0,0,ll.ans+rr.ans,ll.d^rr.d }; int dl = ll.d; int dr = rr.d; xx.L[0] = ll.L[0] + rr.L[dl?1:0]; xx.L[1] = ll.L[1] + rr.L[dl?0:1]; xx.R[0] = rr.R[0] + ll.R[dr?1:0]; xx.R[1] = rr.R[1] + ll.R[dr?0:1]; xx.ans=(xx.ans+(ll.R[1]*rr.L[0] )%mod+ (ll.R[0]*rr.L[1])%mod)%mod; return xx; } int build(int ii,int l,int r) { int p = ++owo; int mid = (l+r)>>1; if(l<r) { z[p].ls = build(ii,l,mid); z[p].rs = build(ii,mid+1,r); z[p] = merge(z[p].ls,z[p].rs); } else { int dd = aacnt[l][ii]; z[p].L[dd]=z[p].R[dd]=1; z[p].d=dd; z[p].ans = dd; } return p; } void change(int p,int l,int r,int d,int k) { if(l<r) { int mid = (l+r)>>1; if(d<=mid) change(z[p].ls,l,mid,d,k); else change(z[p].rs,mid+1,r,d,k); z[p] = merge(z[p].ls,z[p].rs); } else { cle(p); z[p].L[k]=z[p].R[k]=1; z[p].d=k; z[p].ans = k; return ; } } node query(int p,int l,int r,int x,int y) { if(x<=l&&r<=y) return z[p]; int mid = (l+r)>>1; if(y<=mid) return query(z[p].ls,l,mid,x,y); if(x>=mid+1) return query(z[p].rs,mid+1,r,x,y); return mermer( query(z[p].ls,l,mid,x,y) , query(z[p].rs,mid+1,r,x,y) ); } void ccc(int pos,int toto) { int abb[13]; for(int j=1;j<=maxcnt;j++) { abb[j] = toto&1; toto/=2; } for(int j=1;j<=maxcnt;j++) { if(aacnt[pos][j]!=abb[j]) aacnt[pos][j] = abb[j],change(rt[j],1,n,pos,abb[j]); } } int qqq(int l,int r) { int sum = 0; for(int j=1;j<=maxcnt;j++) { node tmp = query(rt[j],1,n,l,r); sum = (sum + bit[j-1]*tmp.ans%mod)%mod; } return sum; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&aaa[i]); bit[0]=1; for(int i=1;i<=maxcnt;i++) bit[i] = bit[i-1]*2%mod; for(int i=1;i<=n;i++) { for(int j=1;j<=maxcnt;j++) { aacnt[i][j] = aaa[i]&1; aaa[i]/=2; } } for(int i=1;i<=maxcnt;i++) rt[i]=build(i,1,n); for(int i=1;i<=m;i++) { int tim,x,y; scanf("%d%d%d",&tim,&x,&y); if(tim==1) ccc(x,y); else printf("%d\n",qqq(x,y)); } }T5 100738E 大致题意:一个n个点m条边的无向图,每条边有一个权值。初始时,点i处有巴士i和司机i,你可以有两种操作:(1)Drive b x y,把巴士b从x点开到y点并付出这条边的权值的费用;(2)Move d x y,司机d从巴士x移动的巴士y上,没有花费 所有司机要集中在一个点,并且每个司机的换乘次数不多于25 求最小花费,并给出一种合理的方案 最小花费显然不就是一棵最小生成树吗?我们考虑方案,发现次数不多于25次,这很像是一个log级别的数,再一想,对于drive如果我们固定到达一个点(如1)那么最后的drive基本就是固定的,我们考虑换乘车的司机。类似于启发式合并,我们每次只要将人少的车暴力模拟插到人多的车里面就可以了,这样一次操作至少人数*2,意味着一个司机换乘次数不会超过logn次,就可以完成。怎么搞?暴力模拟,两两比较,直接乱搞。
#include<cstdio> #include<iostream> #include<algorithm> #include<vector> using namespace std; const int maxn = 800005; int n,m; int bcf[maxn]; int gf(int x) { return bcf[x] == x ? x : bcf[x] = gf(bcf[x]); } struct node { int st,to,len; }z[maxn]; bool cmp(node aa,node bb) { return aa.len < bb.len ; } int siz[maxn]; int ans1; vector<int>ve[maxn]; vector<int>sj[maxn]; int bus[maxn]; void move(int x,int y)//��x���ƶ���y���� { for(int i=sj[x].size()-1;i>=0;i--) { int z = sj[x][i]; printf("Move %d %d %d\n",z,x,y); sj[y].push_back(z); bus[z] = y; } siz[y]+=siz[x]; siz[x]=0; sj[x].clear(); } void dfs(int x,int ba) { siz[x] = 1; for(int i=ve[x].size()-1;i>=0;i--) { int y = ve[x][i]; if(y==ba) continue; dfs(y,x); printf("Drive %d %d %d\n",bus[y],y,x); if(siz[bus[y]]>siz[bus[x]]) move(bus[x],bus[y]); else move(bus[y],bus[x]); } } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) bcf[i]=i; for(int i=1;i<=m;i++) { int x,y,c; scanf("%d%d%d",&x,&y,&c); z[i]=(node){ x,y,c }; } sort(z+1,z+1+m,cmp); int cnt = n-1; int i = 1; while(cnt) { int fx = gf(z[i].st); int fy = gf(z[i].to); if(fx!=fy) { ans1+=z[i].len; ve[z[i].st].push_back(z[i].to); ve[z[i].to].push_back(z[i].st); --cnt; bcf[fx] = fy; } ++i; } for(int i=1;i<=n;i++) sj[i].push_back(i),bus[i]=i; //bus[i]��ʾ��i��˾�����ij� //sj[i]��ʾһ����������˾���ļ��� printf("%d\n",ans1); dfs(1,0); printf("Done"); }T6gym100971M 序列拆分 题意:求出给定序列的每一个前缀最少能划分为多少个good string(如果可以的话)(good string 恰好包含k种字母),否则输出-1 我写的有些麻,我们开两个指针l和r(事实上只用一个)表示l之间到i(目前讨论到的位置)都是个好串,f[i] = min(f[j-1]+1)(f[j-1]!=-1)可以用线段树nLogn维护,事实上f满足单调性,我们只需要找到最左边不等于-1的位置就可以了(所以只用一个指针),时间复杂度O(n)
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int maxn = 400005; int f[maxn]; int bbl[30]; int bbr[30]; int bb[30]; int k; char ss[maxn]; int main() { scanf("%d",&k); int zzl=1,zzr=1; scanf("%s",ss+1); int n = strlen(ss+1); for(int i=1;i<=n;i++) ss[i]-='a'+1; int suml = 0; int sumr = 0; f[0]=0; int sum = 0; for(int i=1;i<=n;i++) { bbl[ss[i]]++; bbr[ss[i]]++; bb[ss[i]]++; if(bbl[ss[i]]==1) ++suml; if(bbr[ss[i]]==1) ++sumr; if(bb[ss[i]]==1) ++sum; while(suml>k&&zzl<i) { bbl[ss[zzl]]--; if(bbl[ss[zzl]]==0) --suml; zzl++; } bool aha = 0; while(sumr>=k&&zzr<=i) { bbr[ss[zzr]]--; if(bbr[ss[zzr]]==0) --sumr; zzr++; aha = 1; } if(aha) { zzr--; bbr[ss[zzr]]++; if(bbr[ss[zzr]]==1) ++sumr; } f[i] = 0x3f3f3f3f; if(suml==k) { for(int j=zzl;j<=zzr;j++) { if(f[j-1]!=-1) { f[i] = f[j-1]+1; break; } } if(f[i]==0x3f3f3f3f) { if(sum==k) f[i] = 1; else f[i] = -1; } } else f[i] = -1; } for(int i=1;i<=n;i++) printf("%d ",f[i]); }T6:LOJ2494「AHOI / HNOI2018」寻宝游戏 题意:自己看 仔细观察,发现&0和|1是不会对答案产生影响的 再观察 若是某一位要得到1,那么最后一个|1的位置必须比最后一个&0的位置更靠后 同理,若是要得到0,那么最后一个&0必须后于最后一个|1 那么我们讲所有运算符转换成01串,|为0,&为1,如果我们将所有数的顺序倒过来,把右端当作高位的情况下,我们按位一个一个讨论(位数间不影响) 运算结果为1当且仅当运算符串的二进制数大小(也可以说字典序)严格小于当前位的数构成的二进制数 同理,运算结果为0当且仅当运算符串的二进制数大小大于等于当前位的数构成的二进制数 于是我们只需要在这些不等式中取一个交集就可以了。 (记得特判所有结果都为0的情况,在这个情况下如果什么操作都不做也算一种方案)
/* 如果我们将|运算当0 &运算当1(把串都倒过来) 那么一定有结果为1时,运算串大小严格小于原串 那么一定有结果为0时,运算穿大小严格dengyu原串 */ #include<stdio.h> #include<bits/stdc++.h> using namespace std; typedef long long LL; const LL mod = 1000000007; LL n,m,q; string ss[5005]; string myw[5005]; string qs; string maxx,minn; LL get(string s) { long long ans = 0; for(LL i=0;i<n;i++) ans = (ans*2LL+s[i])%mod; return ans; } main() { // freopen("aha.txt","w",stdout); scanf("%lld%lld%lld",&n,&m,&q); for(LL i=1;i<=n;i++) maxx+=(char)1,minn+=(char)0; for(LL i=1;i<=n;i++) { cin>>ss[i]; for(LL j=m-1;j>=0;j--) ss[i][j]-='0'; } for(LL i=0;i<m;i++) { for(LL j=n;j>=1;j--) myw[i]+=ss[j][i]; //cout<<233<<endl; //cout<<myw[i]<<endl; } string tmpmin,tmpmax; while(q--) { tmpmin=minn; tmpmax=maxx; cin>>qs; int cnt = 0; for(LL j=0;j<m;j++) { qs[j]-='0'; if(qs[j]) { ++cnt; tmpmax = min(tmpmax,myw[j]); } else tmpmin = max(tmpmin,myw[j]); } long long ans = ((get(tmpmax)-get(tmpmin))%mod+mod)%mod; if(tmpmax<tmpmin) ans = 0; if(!cnt) ++ans; printf("%lld\n",ans); } }T8 Exclusive-OR

#include<iostream> #include<stack> #include<cstdio> #include<algorithm> #define mp make_pair using namespace std; int n,q; int fa[20005],cc[20005]; bool flag = 0; int gf(int x) { if(fa[x]==x) return x; int tmp = gf(fa[x]); cc[x] ^= cc[fa[x]]; return fa[x] = tmp; } char s[105];int ko[20]; void solve1(int p,int v) { if(flag) return; int fx = gf(p); if(fa[fx]==n) { if((cc[p]^cc[fx])==v) return; else flag = 1; } else { fa[fx] = n; cc[fx] = cc[p]^v; } } void solve2(int p,int q,int v) { if(flag) return; int fx = gf(p); int fy = gf(q); if(fx==fy) { if((cc[p]^cc[q])!=v) flag = 1; return; } if(fa[fx]==n&&fa[fy]==n) { if((cc[p]^cc[q])!=v) { flag = 1; } else return; } else if(fa[fx]==n) { fa[fy] = fx; cc[fy] = cc[p]^cc[q]^v; } else if(fa[fy]==n) { fa[fx] = fy; cc[fx] = cc[p]^cc[q]^v; } else { fa[fx] = fy; cc[fx] = cc[p]^cc[q]^v; } } int moj[20005]; void solve3(int k) { if(flag) return; int ans = 0; for(int i=1;i<=k;i++) { int fx = gf(ko[i]); if(fa[fx]==n) { ans ^= cc[ko[i]]; } else { moj[fx]++; } } bool ff = 0; for(int i=1;i<=k;i++) { int fx = gf(ko[i]); if(fa[fx]!=n) { if(moj[fx]&1) { puts("I don't know."); ff = 1; break; } else { ans ^= cc[ko[i]]; } } } for(int i=1;i<=k;i++) { int fx = gf(ko[i]); if(fa[fx]!=n) { moj[fx]--; } } if(ff) return; printf("%d\n",ans); } int main() { // freopen("pai.in","r",stdin); // freopen("zhengjie.out","w",stdout); int T = 0; while(233) { scanf("%d%d\n",&n,&q); if(n+q==0) return 0; printf("Case %d:\n",++T); flag = 0; for(int i=0;i<=n;i++) { fa[i] = i; cc[i] = 0; } int wc = 0; for(int i=1;i<=q;i++) { scanf("%s",&s[0]); int a,b,v=-23333; if(s[0]=='I') { if(!flag) wc++; fgets(s,50,stdin); sscanf(s,"%d%d%d",&a,&b,&v); if(flag) continue; if(v==-23333) solve1(a,b); else solve2(a,b,v); } else { int k; scanf("%d",&k); for(int j=1;j<=k;j++) scanf("%d",&ko[j]); if(flag) continue; solve3(k); } } if(flag) { printf("The first %d",wc); puts(" facts are conflicting."); } } }T9做过,跳过 T10 Roman Digits 打表找规律,当n>=12每次增加49。这个算法noip2017还用了,一定要认真复习(滑稽),这次考试C题也很有趣(shui)可以van一下。 T11 Persistent Bookcase

#include<stdio.h> #include<bits/stdc++.h> using namespace std; const int maxn = 200005; int s[1005][1005]; int la[maxn],en[maxn],owo,nt[maxn]; int ans[maxn]; struct node { int id,x,y; }z[maxn]; void addedge(int a,int b) { en[++owo] = b; nt[owo] = la[a]; la[a] = owo;; }; int cnt =0; int fa[maxn]; int n,m,q; void dfs(int i) { bool aha = 0; if(z[i].id==1) { if(!s[z[i].x][z[i].y]) ++cnt,aha=1; s[z[i].x][z[i].y]=1; } else if(z[i].id==2) { if(s[z[i].x][z[i].y]) --cnt,aha=1; s[z[i].x][z[i].y]=0; } else if(z[i].id==3) { for(int j=1;j<=m;j++) { s[z[i].x][j]^=1; if(s[z[i].x][j]) ++cnt; else --cnt; } } ans[i] = cnt; for(int it=la[i];it;it=nt[it]) { dfs(en[it]); } if(z[i].id==1) { if(aha) { --cnt; s[z[i].x][z[i].y]=0; } } else if(z[i].id==2) { if(aha) { ++cnt; s[z[i].x][z[i].y]=1; } } else if(z[i].id==3) { for(int j=1;j<=m;j++) { s[z[i].x][j]^=1; if(s[z[i].x][j]) ++cnt; else --cnt; } } } int main() { scanf("%d%d%d",&n,&m,&q); for(int i=1;i<=q;i++) { scanf("%d",&z[i].id); if(z[i].id==1) { scanf("%d%d",&z[i].x,&z[i].y); fa[i]=i-1; addedge(i-1,i); } else if(z[i].id==2) { scanf("%d%d",&z[i].x,&z[i].y); fa[i]=i-1; addedge(i-1,i); } else if(z[i].id==3) { scanf("%d",&z[i].x); fa[i] = i-1; addedge(i-1,i); } else { int x; scanf("%d",&x); fa[i] = x; addedge(x,i); } } dfs(0); for(int i=1;i<=q;i++) printf("%d\n",ans[i]); }T12:div2 368E 不想写不想写orz orz orz 因为询问操作不多于2000,那么我们需要将修改操作时间复杂度尽量降低就可了,我们预处理每一块灯在各个询问区域内的欢乐值总和(二位树状数组),对于开关灯,直接O(1)修改bool,查询直接遍历每一块灯的开关情况,加入答案owo END.(END?)