xcpc区域赛训练记录
有训练记录在之前的寒假复健随笔里
因为寒假过了所以开了个新随笔。
ICPC2020济南
C stone game
全部转成在模意义下
发现把和尽量转化成3是最优的
然后就先让和配对,剩下单独的和再自己想办法凑成
#include <bits/stdc++.h> using namespace std; long long a,b,c,ans; int main(){ scanf("%lld%lld%lld",&a,&b,&c); if (a>=b){ long long y=a-b; ans=ans+2ll*b; ans=ans+(y/3)*3; long long t=y%3; if (t==2) ans++; } else{ long long y=b-a; ans=ans+2ll*a; ans=ans+(y/3)*6ll; long long t=y%3; if (t==2) ans=ans+4ll; } cout<<ans; return 0; }
D.Fight against involution
合理内卷(
最native的想法肯定是全取,但是显然不行,因为可能会导致排名发生变化
于是我们按照先排序,这样得到的就是最高排名
然后对于每个位置,它至少要取到前面的中的来保证自己的排名,因为所有人都是最轻松的选择
直接模拟即可。
#include <bits/stdc++.h> using namespace std; int N; long long ans; struct Node{ long long l,r; }a[100005]; int temp(Node a,Node b){ return (a.r<b.r||(a.r==b.r)&&(a.l>b.l)); } int main(){ scanf("%d",&N); for (int i=1;i<=N;i++) scanf("%lld%lld",&a[i].l,&a[i].r); sort(a+1,a+N+1,temp); long long nw=0; for (int i=1;i<=N;i++){ if (a[i].l>nw) nw=a[i].l; ans+=nw; } cout<<ans; return 0; }
L.Bit Sequence
如果没有的限制是个显然的数位
但是有
有点像之前讲的一个题
低位不影响高位
所以我们先把前个数字暴力拿出来,,然后把这个数字拆成左右半边考虑,左边数位统一统计一下即可。
代码是学长写的
#include<bits/stdc++.h> #define ll long long using namespace std; ll dp[64][2][128][2][2]; int v[65],a[105]; int m; ll cal(int st,int s,int t) { int f=1; for(int i=0;i<m&&f;i++) { if(st+i<128) f&=(__builtin_parity(st+i)^s)==a[i]; else f&=(__builtin_parity(st+i)^s^t)==a[i]; } return f; } ll dfs(int pos,int limit,int st,int s,int t) { if(pos==-1) return cal(st,s,t) ; ll &res=dp[pos][limit][st][s][t]; if(res!=-1) return res; res=0; int up=limit?v[pos]:1; for(int i=0;i<=up;i++) if(pos>6) res+=dfs(pos-1,limit&i==up,(st*2+i)&127,s^i,i&(!t)); else res+=dfs(pos-1,limit&i==up,(st*2+i)&127,s,t); //cout<<res<<'\n'; return res; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int T; cin>>T; while(T--) { ll L; cin>>m>>L; for(int i=0;i<m;i++) cin>>a[i]; memset(dp,-1,sizeof dp); int len=0; for(;L;L>>=1)v[len++]=L&1; cout<<dfs(len-1,1,0,0,0)<<'\n'; } }
赛后补题:
A.atrix Equation
草了,我高消的板子有问题
发现操作2可以转化成,操作1可以转化成
于是我们可以有一个暴力的想法,解个异或方程组
然后仔细思考,不难发现同一列每个方程组是一组的,不涉及其他列的变量
于是就变成解次,一次个方程组
这东西可以直接,优化一下即可。
高消板子有问题调了大半场没出,麻了
#include <bits/stdc++.h> using namespace std; const long long fish=998244353; bitset<205> a[205]; int aa[205][205],b[205][205]; long long anss=0; int N; long long Pow(long long x,long long y){ long long ans=1; while (y){ if (y&1ll) ans=1ll*ans*x%fish; x=1ll*x*x%fish; y>>=1ll; } return ans; } long long Gauss(long long n){ int t=1; long long now=1,ans=0; for (int i=1;i<=n;i++){ now=t; while (!a[now][i]&&now<=n) now++; if (now==n+1) continue; if (now!=t) swap(a[now],a[t]); for (int j=t+1;j<=n;j++){ if (a[j][i]) a[j]^=a[t]; } ans++; t++; } return n-ans; } int main(){ scanf("%d",&N); for (int i=1;i<=N;i++) for (int j=1;j<=N;j++) scanf("%d",&aa[i][j]); for (int i=1;i<=N;i++) for (int j=1;j<=N;j++) scanf("%d",&b[i][j]); for (int i=1;i<=N;i++){ for (int j=1;j<=N;j++){ for (int k=1;k<=N;k++){ a[j][k]=aa[j][k]; } a[j][j]=(aa[j][j]-b[j][i]+2)%2; } long long T=Gauss(1ll*N); anss+=T; } cout<<Pow(2ll,anss); return 0; }
2019ccpc秦皇岛
签到,判断这个数的因子是不是只有即可
#include <bits/stdc++.h> using namespace std; int T,N; int main(){ scanf("%d",&T); while (T--){ scanf("%d",&N); while (N%2==0) N/=2; while (N%5==0) N/=5; if (N!=1) printf("Yes\n"); else printf("No\n"); } return 0; }
F. Forest Program
分类讨论。
首先把所有环取出来
发现其实要是森林的话,不能有环
其实就是每个环至少断一个边
对一个有个边的环来说
这个答案是
剩下的边随便取
怎么找环?
类似tarjan,dfs的时候找到一个访问过的位置大力弹栈就行
因为仙人掌图所以显然是正确的。
代码学长写的
#include <bits/stdc++.h> using namespace std; const int P= 998244353; const int N=1e6+10; int ans=1,cnt=0; vector<int>E[N]; int vis[N],pre[N]; int power(int x,int y){ int res=1; while(y) { if (y&1) res=1LL*res*x%P; x=1LL*x*x%P; y>>=1; } return res; } void dfs(int u,int f){ vis[u]=1; for(auto v:E[u]) { if(v==f||vis[v]==2) continue; if(vis[v]==1) { int now=u; int sz=1; while(now!=v)now=pre[now],sz++; cnt+=sz; ans=(1LL*ans*(power(2,sz)-1)%P+P)%P; }else pre[v]=u,dfs(v,u); } vis[u]=2; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n,m; cin>>n>>m; for(int i=1;i<=m;i++) { int u,v; cin>>u>>v; E[u].push_back(v); E[v].push_back(u); } for(int i=1;i<=n;i++) if(!vis[i]) dfs(i,0); ans=1LL*ans*power(2,m-cnt)%P; cout<<ans<<'\n'; }
K. MUV LUV UNLIMITED
很有趣的博弈题.jpg
首先,如果一个树全是由偶数链构成,那么显然后手必胜
全是奇数链则先手必胜
考虑奇偶交替的情况
显然,先手可以决策局势
因为我可以把所有的奇数取成偶数
然后就有一个native的想法, 大力考虑所有叶子节点到根的距离的奇偶性,然后全是偶数才后手必胜,否则先手必胜
然后被样例叉出去了(
为什么?
因为路径会有交叉
那么我们就每次考虑一个子问题,即每次考虑一个点,下面挂的全是链
显然这个子情况符合我上面说的东西
那么我们要怎么合并子情况?
实际上还是只有全是偶数的时候后手必胜,因为这时候后手在每一轮子问题里都必胜
否则先手是可以决定到一个子问题的终点的时候自己是先手还是后手的,而这会决定下一个子问题的胜负。
于是做法就是:对所有子问题考虑,如果路径全是偶数的话,那么后手胜利
否则先手必胜。
#include <bits/stdc++.h> using namespace std; int N,T; int odd,even; vector<int> Son[1000005]; void dfs(int Now,int Len,int dep){ if (Son[Now].size()==0){ int Lenn=Len-dep; if (Lenn&1) odd=1; return; } for (auto x:Son[Now]){ if (Son[Now].size()>1) dfs(x,Len+1,Len); else dfs(x,Len+1,dep); if (odd) return; } } int main(){ scanf("%d",&T); while (T--){ odd=0,even=0; scanf("%d",&N); for (int i=1;i<=N;i++) Son[i].clear(); for (int i=2;i<=N;i++){ int fa; scanf("%d",&fa); Son[fa].push_back(i); } dfs(1,1,0); if (!odd) { printf("Meiya\n"); } else printf("Takeru\n"); } return 0; }
赛后补题:
E.Escape
核心是发现至多两个机器人经过同一个位置,且这两个机器人的方向一定不同(可以画画图或者反证)
然后发现,我们考虑网络流
把一个位置拆成两个点,横点和竖点
每个点可以向四个方向连一个流量为1的边,表示正常走路
横点竖点间连一个流量为1的边,表示换向。
然后每个终点向连边,流量为,向起点,流量为
代码是赛中队友写的,赛后调了调
赛中没考虑终点能否到达,而且到终点的流量连成了
#include <iostream> #include <queue> #include <cstring> using namespace std; const int N=1e5+10,M=1e6+10,INF=1e9; int n,m,S,T,cnt; int head[N],d[N],cur[N]; struct edges { int v,nxt,f; }e[M*2]; void add(int u,int v,int f) { e[cnt].v=v,e[cnt].nxt=head[u],e[cnt].f=f,head[u]=cnt++; e[cnt].v=u,e[cnt].nxt=head[v],e[cnt].f=0,head[v]=cnt++; } bool bfs() { memset(d,-1,sizeof d); queue<int>q; q.push(S); d[S]=0,cur[S]=head[S]; while(q.size()) { int u=q.front(); q.pop(); for(int i=head[u];~i;i=e[i].nxt) { int v=e[i].v,f=e[i].f; if(d[v]==-1&&f) { d[v]=d[u]+1; cur[v]=head[v]; if(v==T) return true; q.push(v); } } } return false; } int find(int u,int limit) { if(u==T) return limit; int flow=0; for(int i=cur[u];~i&&flow<limit;i=e[i].nxt) { cur[u]=i; int v=e[i].v,f=e[i].f; if(d[v]==d[u]+1&&f) { int t=find(v,min(f,limit-flow)); if(!t) d[v]=-1; e[i].f-=t,e[i^1].f+=t,flow+=t; } } return flow; } int dinic() { int ans=0,flow; while(bfs()) while(flow=find(S,INF)) ans+=flow; return ans; } int x[105][105],y[105][105]; char g[105][105]; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin>>t; while(t--) { memset(head,-1,sizeof head); cnt=0; int a,b; cin>>n>>m>>a>>b; S=0,T=n*m*2+1; int idx=0; for(int i=1;i<=n;i++) cin>>g[i]+1; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) x[i][j]=++idx,y[i][j]=++idx; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { if(g[i][j]=='1') continue; if(j-1>0&&g[i][j-1]=='0') add(x[i][j],x[i][j-1],1); if(j+1<=m&&g[i][j+1]=='0') add(x[i][j],x[i][j+1],1); if(i-1>0&&g[i-1][j]=='0') add(y[i][j],y[i-1][j],1); if(i+1<=n&&g[i+1][j]=='0') add(y[i][j],y[i+1][j],1); add(x[i][j],y[i][j],1); add(y[i][j],x[i][j],1); } for(int i=1;i<=a;i++) { int x; cin>>x; if(g[1][x]=='1') continue; add(S,y[1][x],1); } for(int i=1;i<=b;i++) { int x; cin>>x; if(g[n][x]=='1') continue; add(y[n][x],T,1); } if(dinic()<a) cout<<"No"; else cout<<"Yes"; cout<<'\n'; }
#include <bits/stdc++.h> using namespace std; int dp[1<<24],N; int w[12][12]; char a[12][12]; int main(){ scanf("%d",&N); for (int i=0;i<N;i++) scanf("%s",a[i]); for (int i=0;i<N;i++) for (int j=0;j<N;j++) scanf("%d",&w[i][j]); int T=(1<<N)-1; int S=(T<<N); for (int i=T;i<=S;i++) dp[i]=1e9; dp[S]=0; for (int st=S;st>T;st--){ if (dp[st]==1e9) continue; int cnt=0; for (int i=0;i<=2*N;i++) if (st>>i&1) cnt++; if (cnt!=N) continue; int x=-1,y=0; for (int i=2*N-1;i>=1;i--){ if ((st>>i)&1) x++; else y++; if ((st>>i)&1 && !((st>>(i-1))&1)){ dp[st-(1<<(i-1))]=min(dp[st-(1<<(i-1))],dp[st]+(a[x][y]!='.')*w[x][y]); int nx=-1,ny=0; for (int j=2*N-1;j>i+1;j--){ if ((st>>j)&1) nx++; else ny++; if (((st>>j)&1) && (!((st>>(j-1))&1)) && (a[x][y] + a[nx][ny] == 'B' + 'W')){ dp[st-(1<<(i-1))-(1<<(j-1))]=min(dp[st-(1<<(i-1))-(1<<(j-1))],dp[st]+abs(w[x][y]-w[nx][ny])); } } } } } cout<<dp[T]; return 0; }
icpc2020首尔
B. Commemorative Dice
签到,暴力枚举
C. Dessert Café
因为两点之间在树上只有一条路径,稍微画画图就会发现答案的点其实就是关键点之间的点。
#include <bits/stdc++.h> using namespace std; int ans=0; int N,K; int pd[100005],cnt,Arrive[200005],nex[200005],las[200005]; bool col[200005]; void dfs(int Now,int fa){ int cnt=0,cnt1=0; for (int i=las[Now];i;i=nex[i]){ int v=Arrive[i]; if (v==fa) continue; dfs(v,Now); pd[Now]+=pd[v]; if (pd[v]) cnt++; } if (col[Now]) pd[Now]++; if (K-pd[Now]!=0) cnt++; if (cnt>=2||col[Now]) ans++; } void jt(int x,int y){ cnt++; nex[cnt]=las[x]; las[x]=cnt; Arrive[cnt]=y; } int main(){ scanf("%d%d",&N,&K); for (int i=1;i<N;i++){ int u,v,w; scanf("%d%d%d",&u,&v,&w); jt(u,v); jt(v,u); } for (int i=1;i<=K;i++){ int x; scanf("%d",&x); col[x]=true; } dfs(1,1); printf("%d\n",ans); return 0; }
G. Mobile Robot
固定起点后,每个点都确定了,每个点还是会走到它对应的那个位置上去
考虑从起点向右延伸,则对于每个点来说,它走的距离就是
是我们的起点
最小化这个的最大值
我们转化一下,其实就是有个新点,每个点是,找一个点到最离它远点距离最近
显然是区间中点。
左延伸类似的做。
注意精度。
#include <bits/stdc++.h> using namespace std; int N; long long pos[1000005],d; long long pos1[1000005],pos2[1000005]; int main(){ scanf("%d%lld",&N,&d); for (int i=1;i<=N;i++){ scanf("%lld",&pos[i]); pos1[i]=pos[i]-(i-1)*d; pos2[i]=pos[i]+(i-1)*d; } sort(pos1+1,pos1+N+1); sort(pos2+1,pos2+N+1); long long ans1=0; ans1=(pos1[N]-pos1[1])/2; long long anss=(pos2[N]-pos2[1])/2; long long Ans; int flag=0; if (anss<ans1){ Ans=anss; if ((pos2[N]-pos2[1])%2==0) flag=0; else flag=1; } else{ Ans=ans1; if ((pos1[N]-pos1[1])%2==0) flag=0; else flag=1; } printf("%lld.%d\n",min(anss,ans1),flag*5); return 0; }
H. Needle
转化一下式子,统计的三元组
考虑固定
即可。
//#pragma GCC optimize(2) #include <bits/stdc++.h> #define ll long long #define pb push_back using namespace std; const int N = 1100000; const int p = 998244353, gg = 3, ig = 332738118, img = 86583718;//1004535809 const int M=18e5; const int mod=998244353; template <typename T>void rd(T &x) { x = 0; int f = 1; char ch = getchar(); while(ch < '0' || ch > '9') {if(ch == '-')f = -1;ch = getchar();} while(ch >= '0' && ch <= '9') {x = x * 10 + ch - '0';ch = getchar();} x *= f; } ll qpow(ll a, int b) { ll res = 1; while(b) { if(b & 1) res = 1ll * res * a % mod; a = 1ll * a * a % mod; b >>= 1; } return res; } namespace Poly { #define mul(x, y) (1ll * x * y >= mod ? 1ll * x * y % mod : 1ll * x * y) #define minus(x, y) (1ll * x - y < 0 ? 1ll * x - y + mod : 1ll * x - y) #define plus(x, y) (1ll * x + y >= mod ? 1ll * x + y - mod : 1ll * x + y) #define ck(x) (x >= mod ? x - mod : x)//取模运算太慢了 typedef vector<ll> poly; const int G = 3;//根据具体的模数而定,原根可不一定不一样!!! //一般模数的原根为 2 3 5 7 10 6 const int inv_G = qpow(G, mod - 2); int RR[N], deer[2][21][N], inv[N]; void init(const int t) {//预处理出来NTT里需要的w和wn,砍掉了一个log的时间 for(int p = 1; p <= t; ++ p) { int buf1 = qpow(G, (mod - 1) / (1 << p)); int buf0 = qpow(inv_G, (mod - 1) / (1 << p)); deer[0][p][0] = deer[1][p][0] = 1; for(int i = 1; i < (1 << p); ++ i) { deer[0][p][i] = 1ll * deer[0][p][i - 1] * buf0 % mod;//逆 deer[1][p][i] = 1ll * deer[1][p][i - 1] * buf1 % mod; } } inv[1] = 1; for(int i = 2; i <= (1 << t); ++ i) inv[i] = 1ll * inv[mod % i] * (mod - mod / i) % mod; } int NTT_init(int n) {//快速数论变换预处理 int limit = 1, L = 0; while(limit < n) limit <<= 1, L ++ ; for(int i = 0; i < limit; ++ i) RR[i] = (RR[i >> 1] >> 1) | ((i & 1) << (L - 1)); return limit; } void NTT(poly &A, int type, int limit) {//快速数论变换 A.resize(limit); for(int i = 0; i < limit; ++ i) if(i < RR[i]) swap(A[i], A[RR[i]]); for(int mid = 2, j = 1; mid <= limit; mid <<= 1, ++ j) { int len = mid >> 1; for(int pos = 0; pos < limit; pos += mid) { int *wn = deer[type][j]; for(int i = pos; i < pos + len; ++ i, ++ wn) { int tmp = 1ll * (*wn) * A[i + len] % mod; A[i + len] = ck(A[i] - tmp + mod); A[i] = ck(A[i] + tmp); } } } if(type == 0) { for(int i = 0; i < limit; ++ i) A[i] = 1ll * A[i] * inv[limit] % mod; } } inline poly poly_mul(poly A, poly B) {//多项式乘法 int deg = A.size() + B.size() - 1; int limit = NTT_init(deg); poly C(limit); NTT(A, 1, limit); NTT(B, 1, limit); for(int i = 0; i < limit; ++ i) C[i] = 1ll * A[i] * B[i] % mod; NTT(C, 0, limit); C.resize(deg); return C; } //多个多项式相乘CDQ或者利用优先队列启发式合并 /* inline poly CDQ(int l,int r) { if(l==r) { return poly{1,A[l]}; } int mid=l+r>>1; poly L=CDQ(l,mid); poly R=CDQ(mid+1,r); return poly_mul(L,R); }*/ } using namespace Poly; const int del=30000; poly A(60005),B(120005),C(60005); int main() { //ios::sync_with_stdio(false); //cin.tie(nullptr); init(19); int n1,n2,n3; cin>>n1; for(int i=1;i<=n1;i++) { int x; cin>>x; A[x+del]=1; } cin>>n2; for(int i=1;i<=n2;i++) { int x; cin>>x; B[2*(x+del)]=1; } cin>>n3; for(int i=1;i<=n3;i++) { int x; cin>>x; C[x+del]=1; } poly D=poly_mul(A,C); int res=0; for(int i=0;i<120001;i++) if(D[i]!=0&&B[i]) res+=D[i]; cout<<res<<'\n'; return 0; }
SEERC2020
B. Reverse Game
观察发现,这个的形式类似于冒泡排序,于是考虑逆序对数
发现转一次逆序对数,则
于是问题就变成了,一堆石子,最多一次取个,问你先手后手赢
巴什博弈,结论是数量是的倍数就后手必胜,否则先手。
证明的话考虑是必败态,而先手必定可以构造一个这种状况(如果不是的倍数)
虽然是队友切的
#include<bits/stdc++.h> using namespace std; string s; int main() { cin>>s; int n=s.size(); long long res=0,ans=0; for(int i=n-1; i>=0; i--) { if(s[i]=='0') res++; else ans=ans+res; } if(ans%3==0) cout<<"Bob"<<endl; else cout<<"Alice"<<endl; return 0; }
两两乘积和换成和的平方减去平方的和
然后统计一下前缀和就行。
#include <bits/stdc++.h> using namespace std; int N; long long dp[5][5]; long long Sum,pSum,ans; int main(){ scanf("%d",&N); dp[0][0]=1; for (int i=1;i<=N;i++){ int x; scanf("%d",&x); Sum=(Sum+x)%3; pSum=(pSum+1ll*x*x)%3; for (int j=0;j<=2;j++) for (int k=0;k<=2;k++){ int Now=(((Sum-j+3)%3*(Sum-j+3)%3)%3-(pSum-k+3)%3+3)%3; if (Now==0) ans+=dp[j][k]; } dp[Sum][pSum]++; } printf("%lld\n",ans); }
H. AND = OR
最开始给了个二分权值+树套树的做法,因为太麻烦了被队友叉出去了。
我的做法是,发现单调减,单调增
于是对于每次询问来说,二分那个答案的值
小于答案的全部做,大于的全部做
如果大于 就往右找,否则往左。
问题就变成维护区间中权值前/后缀的连续位运算和,离散化一下线段树套权值线段树应该能做。
队友的聪明做法是发现和单增/减的性质是由于的数量发生改变而产生的
于是直接去维护区间内每个数的的个数的大小关系就好了。
代码还没仔细看,队友写的。
可能回头会去把自己的做法实现一下(咕咕咕
#include<bits/stdc++.h> using namespace std; const int maxn=1e5+5; const int MX=30; const int tot_max=2e6+5; struct Node { int val,num,i; }; int n,q; Node a[maxn]; int tot_or,tot_and,tree_or[tot_max],tree_and[tot_max],tree_cnt[tot_max],son_or[tot_max][2],son_and[tot_max][2]; int root_or[MX+2],root_and[MX+2],res_or,res_and,res_cnt; bool cmp_node(const Node &a,const Node &b) { return a.num<b.num; } void tree_xg_or(int k,int last,int l,int r,int x,int z) { while (l<r) { tree_or[k]=tree_or[last]|z; tree_cnt[k]=tree_cnt[last]+1; int mid=(l+r)>>1; if (x<=mid) { son_or[k][1]=son_or[last][1]; son_or[k][0]=++tot_or; k=tot_or, last=son_or[last][0], r=mid; } else { son_or[k][0]=son_or[last][0]; son_or[k][1]=++tot_or; k=tot_or, last=son_or[last][1], l=mid+1; } } tree_or[k]=tree_or[last]|z; tree_cnt[k]=tree_cnt[last]+1; } void tree_xg_and(int k,int last,int l,int r,int x,int z) { while (l<r) { tree_and[k]=tree_and[last]&z; int mid=(l+r)>>1; if (x<=mid) { son_and[k][1]=son_and[last][1]; son_and[k][0]=++tot_and; k=tot_and, last=son_and[last][0], r=mid; } else { son_and[k][0]=son_and[last][0]; son_and[k][1]=++tot_and; k=tot_and, last=son_and[last][1], l=mid+1; } } tree_and[k]=tree_and[last]&z; } void tree_cx(int kOr,int lastOr,int kAnd,int l,int r,int x,int y) { if (x<=l && r<=y) { res_or|=tree_or[kOr]; res_and&=tree_and[kAnd]; res_cnt+=tree_cnt[kOr]-tree_cnt[lastOr]; return; } int mid=(l+r)>>1; if (x<=mid) tree_cx(son_or[kOr][0],son_or[lastOr][0],son_and[kAnd][0],l,mid,x,y); if (mid<y) tree_cx(son_or[kOr][1],son_or[lastOr][1],son_and[kAnd][1],mid+1,r,x,y); } int sum_or[MX+5],sum_and[MX+5],cnt[MX+5]; int main() { scanf("%d %d",&n,&q); for(int i=1; i<=n; i++) { scanf("%d",&a[i].val); a[i].num=__builtin_popcount(a[i].val); a[i].i=i; } sort(a+1,a+1+n,cmp_node); int i=1; for(int w=0; w<=MX; w++) { if (w) root_or[w]=root_or[w-1]; for(; i<=n && a[i].num==w; i++) { int last=root_or[w]; tree_xg_or(root_or[w]=++tot_or,last,1,n,a[i].i,a[i].val); } } tree_and[0]=(1<<30)-1; i=n; for(int w=MX; w>=0; w--) { root_and[w]=root_and[w+1]; for(; i && a[i].num==w; i--) { int last=root_and[w]; tree_xg_and(root_and[w]=++tot_and,last,1,n,a[i].i,a[i].val); } } while (q--) { int l,r; scanf("%d %d",&l,&r); int sumCnt=0; for(int w=0; w<=MX; w++) { res_or=0, res_and=(1<<30)-1, res_cnt=0; tree_cx(root_or[w],(w==0 ?0 :root_or[w-1]),root_and[w],1,n,l,r); sum_or[w]=res_or; sum_and[w]=res_and; cnt[w]=res_cnt; sumCnt+=res_cnt; } bool ans=0; int numLess=0; for(int w=0; w<=MX; w++) { if (numLess && numLess<sumCnt && sum_or[w-1]==sum_and[w]) { ans=1; break; } if (sum_or[w]==sum_and[w] && cnt[w]>=2) { ans=1; break; } numLess+=cnt[w]; } puts(ans ?"YES" :"NO"); } }
L. Neo-Robin Hood
考虑一个的想法。
考虑按照从大到小排,从小到大排。
那么,如果一个人能同时被偷/资助的话,那么肯定用一个双指针顺着取是最优的。
那么,我就只需要考虑出现冲突的情况。
考虑对于一个位置,它同时需要被偷和被资助
那么我们假设
偷,帮
帮,偷两种情况。
前一种情况的贡献是,后一种情况的贡献是
因为和都是还没被取过的状态,且它们是当前最优
于是一定有,
那么不妨设取第一种情况
有>
再根据式子,则一定有
移项
则有
满足这个条件的时候就偷,否则就帮
于是我们不妨按照排序
在这种序列下,排序靠后的一定会相对排序靠前的优先被偷
于是我们枚举一个分段点,前半段找最大,后半段找最小即可。
随便说的,主要思路是队友提的,代码也是队友写的。
感觉自己这个证法很乱。
#include <bits/stdc++.h> #define ll long long using namespace std; struct node{ int m,p; bool operator < (const node &t) const { return m+p<t.m+t.p; } }; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n; cin>>n; vector<node>poli(n); for(int i=0;i<n;i++) cin>>poli[i].m; for(int i=0;i<n;i++) cin>>poli[i].p; sort(poli.begin(), poli.end()); int l=0,r=n/2,res=0; auto check=[&](int k)->bool{ if(k==0) return 1; priority_queue<int,vector<int>,greater<int>>M; priority_queue<int>P; vector<ll>sump(n),summ(n); for(int i=0;i<k;i++) { P.push(poli[i].p); sump[k-1]+=poli[i].p; } for(int i=k;i<n;i++) { if(P.top()<=poli[i].p) sump[i]=sump[i-1]; else { sump[i]=sump[i-1]+poli[i].p-P.top(); P.pop(); P.push(poli[i].p); } } for(int i=n-1;i>n-1-k;i--) { M.push(poli[i].m); summ[n-k]+=poli[i].m; } for(int i=n-1-k;i>=0;i--) { if(M.top()>=poli[i].m) summ[i]=summ[i+1]; else { summ[i]=summ[i+1]+poli[i].m-M.top(); M.pop(); M.push(poli[i].m); } } for(int i=k;i<n-k+1;i++) { //cout<<summ[i]<<" "<<sump[i-1]<<'\n'; if(summ[i]>=sump[i-1])return 1; } return 0; }; while(l<=r) { int mid=l+r>>1; if(check(mid)) res=mid,l=mid+1; else r=mid-1; } cout<<res<<'\n'; }
赛后补题:
F-Fence Job
感觉赛中真的被降智了……半天没弄出来。
考虑一个位置,往左右能延伸的最大距离。
考虑操作太复杂了,问题只要求统计答案。
其实问题则转化为,一个位置能把自己的颜色想左向右延伸到的位置
后填充的颜色覆盖先填充的颜色
问方案数。
设表示前个数,最后一段填入的是
转移的时候,先考虑左半边
如果一个位置能延伸到的话,那么它的方案数就是前面的位置能延伸到位置的方案数的和(它替换掉最后一段)
那么,其实考虑把那些延伸不过来的位置也加进来(它们贡献是)
其实就是一个前缀和,维护一下就好了。
然后右半边同理。
赛场上把问题转化出来了,想不出来,自闭了(
#include <bits/stdc++.h> using namespace std; const int fish=1e9+7; int dp[5005][5005]; int N,Sum[5005],h[5005],l[5005],r[5005]; int main(){ scanf("%d",&N); for (int i=1;i<=N;i++){ scanf("%d",&h[i]); } for (int i=1;i<=N;i++){ for (int j=i-1;;j--){ if (h[i]>h[j]) { l[i]=j; break; } } } for (int i=1;i<=N;i++) for (int j=i+1;;j++){ if (h[i]>h[j]) { r[i]=j; break; } } for (int i=1;i<=N;i++){ if (l[i]<1) dp[1][i]++; (Sum[i]=Sum[i-1]+dp[1][i])%=fish; } for (int i=2;i<=N;i++){ for (int j=1;j<=N;j++){ if(l[j]<i&&i<r[j]) (dp[i][j]+=Sum[j])%=fish; Sum[j]=(Sum[j-1]+dp[i][j])%fish; } } int ans=0; for (int i=1;i<=N;i++) (ans+=dp[N][i])%=fish; cout<<ans; return 0; }
CRRC19
感觉做的挺顺的
A. Green tea
签到,没啥好说的
#include <bits/stdc++.h> using namespace std; int T1,T2,ans1,ans2; int minn=1e9+7; int main(){ minn=1e9+7; scanf("%d%d",&T1,&T2); for (int i=0;i<=1000;i++) for (int j=0;j<=1000;j++) if (T1*i+T2*j==80*(i+j)){ if (i==j&&i==0) continue; minn=min(i+j,minn); if (i+j==minn) ans1=i,ans2=j; } printf("%d %d",ans1,ans2); return 0; }
B. Mysterious Resistors
高中物理
发现随着增大,总电阻增大
二分一下就好了
#include <bits/stdc++.h> using namespace std; const double eps=1e-6; double R; int N; double rr[5005]; int Check(double nw){ double anss=0; for (int i=1;i<=N;i++){ double nww=(rr[i]*nw)/(rr[i]+nw); anss+=nww; } //cout<<nw<<" "<<anss<<endl; if (fabs(anss-R)<=eps) return 2; if (anss>R) return 1; else return 0; } int main(){ scanf("%d%lf",&N,&R); double Sum=0; for (int i=1;i<=N;i++){ scanf("%lf",&rr[i]); Sum+=rr[i]; } double l=0,r=Sum,mid; while (l<r){ mid=(l+r)/2; //printf("%lf %lf %lf\n",l,r,mid); if (Check(mid)==2){ printf("%.10lf\n",mid); return 0; } if (Check(mid)==1) r=mid; else l=mid; } return 0; }
C. Emoticons
学长写的,听说是模拟
#include <bits/stdc++.h> using namespace std; string emo[]={"[:|||:]" ,":-\\" ,":-P", ":D" ,":C", "8-0",":-|","%0",":-0" ,":-E",":-X" , ":~(" , ";-)" , ";-(" , ":)" , ":(" }; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); string s; cin>>s; int n=s.size(); vector<int>vis(n+1),pos; for(int i=0;i<16;i++) { while(true) { bool ok=1; pos.clear(); for(int j=0;j<emo[i].size();j++) { bool flag=0; for(int k=0;k<n;k++) { if(s[k]==emo[i][j]&&!vis[k]) { vis[k]=1; flag=1; pos.push_back(k); break; } } ok&=flag; if(!ok) { for(auto x:pos) vis[x]=0; break; } } if(!ok)break; else if(ok) cout<<emo[i]<<'\n'; } } cout<<"LOL\n"; return 0; }
D. Power play
构造一个函数,求个导,算个极值点,然后以极值点为分界左右二分找零点
代码是学长写的
#include <bits/stdc++.h> #define ll long long using namespace std; const double eps=1e-9; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); ll a,b; cin>>a>>b; auto f=[&](auto self,ll l,ll r)->ll{ if(l>r) return 0; ll mid=l+r>>1; double x=1.0*mid*log(a)-1.0*b*log(mid); if(abs(x)<eps) { ll ans=0; ans=self(self,1,mid-1); if(ans) return ans; return mid; } if(1.0*mid*log(a)>1.0*b*log(mid)) return self(self,l,mid-1); return self(self,mid+1,r); }; cout<<f(f,1,1e18)<<'\n'; return 0; }
F. A word game
每个字符都是独立的,问题就等价于有堆石子,每次取,个或者全部
问最终谁赢
算算函数就行了。
#include <bits/stdc++.h> using namespace std; int a[55]; int SG[55]; int GetSG(int x){ int mex[55]; memset(mex,0,sizeof(mex)); if (SG[x]!=-1) return SG[x]; if (x>=2) mex[GetSG(x-2)]=1; if (x>=1) mex[GetSG(x-1)]=1; mex[GetSG(0)]=1; for (int i=0;;i++) if (mex[i]==0) { SG[x]=i; return SG[x]; } } int main(){ string ss; cin>>ss; int Len=ss.length(); for (int i=0;i<Len;i++){ a[ss[i]-'A']++; } memset(SG,-1,sizeof(SG)); SG[0]=0; int ans=0; for (int i=0;i<26;i++){ ans^=GetSG(a[i]); //cout<<a[i]<<" "<<GetSG(a[i])<<endl; } //cout<<SG[2]<<" "<<SG[1]<<endl; if (ans==0) printf("Bob\n"); else printf("Alice\n"); return 0; }
H. Men's showdown
学长弄的,听说也是个博弈
#include <bits/stdc++.h> using namespace std; int dp[10005]; int op[]={1,5,13}; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); dp[0]=1; for(int i=1;i<=10000;i++) { dp[i]=1; int ok=1; for(int j=0;j<3;j++) if(i>=op[j]) ok&=dp[i-op[j]]; if(ok) dp[i]=0; } int n; cin>>n; if(dp[n]) cout<<1<<'\n'; else cout<<2<<'\n'; }
I. Andrew and Python
交互题。
我的想法是先把正方形分成四块三角形
然后每块三角形再每次切两半
这样的话,面积每次至少缩小一半
在级别的次数内可以让三角形的面积趋于,算一下大概多次就行了。
代码是学长写的。
#include <bits/stdc++.h> #define ll long long using namespace std; typedef pair<ll, ll> pll; inline ll read() { ll s = 0, w = 1; char ch = getchar(); while (ch < '0' || ch > '9') { if (ch == '-') w = -1; ch = getchar(); } while (ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar(); return s * w; } ll const maxn = 2e5 + 10; ll n, m, T; pll get_mid(pll x, pll y, pll z) { double resx = (1.0 * x.first + 1.0 * y.first) / 2.0; double resy = (1.0 * x.second + 1.0 * y.second) / 2.0; if (z.first < resx) resx = ceil(resx); else resx = floor(resx); if (z.second < resy) resy = ceil(resy); else resy = floor(resy); return {(ll)resx, (ll)resy}; } ll test(pll a, pll b, pll c) { if ((c.second - b.second) * (b.first - a.first) == (b.second - a.second) * (c.first - b.first)) return 1; else return 0; } ll check(pll a, pll b, pll c) { if (!test(a, b, c)) { cout << "? 3 " << a.first << " " << a.second << " " << b.first << " " << b.second << " " << c.first << " " << c.second << endl; fflush(stdout); string s; cin >> s; if (s == "Yes") return 1; else return 0; } else { if (a == b) { return 0; } else { cout << "? 2 " << a.first << " " << a.second << " " << b.first << " " << b.second << endl; fflush(stdout); string s; cin >> s; if (s == "Yes") return 1; else return 0; } } } ll check2(pll a, pll b, pll c) { ll ab = (a.first - b.first) * (a.first - b.first) + (a.second - b.second) * (a.second - b.second); ll bc = (b.first - c.first) * (b.first - c.first) + (b.second - c.second) * (b.second - c.second); ll ac = (a.first - c.first) * (a.first - c.first) + (a.second - c.second) * (a.second - c.second); if (ab < 4 && bc < 4 && ac < 4) return 0; return 1; } ll Query(pll x) { cout << "? 1 " << x.first << " " << x.second << endl; fflush(stdout); string s; cin >> s; if (s == "Yes") return 1; else return 0; } void print(pll x) { cout << "! " << x.first << " " << x.second << endl; fflush(stdout); } int main() { cin >> n; pll a, b, c; c = {1, 1}, a = {1, n}, b = {n, 1}; if (!check(a, b, c)) c = {n, n}; while (check2(a, b, c)) { pll mid = get_mid(a, b, c); if (check(a, c, mid)) { b = c, c = mid; } else { a = b, b = c, c = mid; } } if (Query(a)) { print(a); } else if (Query(b)) { print(b); } else if (Query(c)) { print(c); } return 0; }
J. Something that resembles Waring's problem
搞出这题我能吹一年好吧(
学长开始看的时候,说考虑能不能是连续的自然数
于是我就设了一个中间值
然后,,三个数的三次方加起来
发现好像有点多余
和的立方和是
于是考虑再拿两个,把数字变成,看起来干净一点
然后就变成拆分出,发现只要和同余就好了。
代码是学长写的,大数
n = input() n = int(n) r = n % 6 m = r * r * r t=(n-m)/6 print('5') print('%d %d %d %d %d' %(r, ( t + 1), (t - 1), (-t), (-t)))
K. Parabolic sorting
总感觉是哪年的题……
从大到小考虑,把最大数往左/右移
然后考虑次大数
如果最大值在它左边的话,那么它这次往左的代价就是,否则是
于是合理推广一下,对于一个位置,我们找出它左边有几个比它大的,用位置减去这些比它大的数字的个数,就是这次它往左的贡献
发现其实这个贡献是比它小的数字的个数,
离散化树状数组维护一下结束(
#include<bits/stdc++.h> using namespace std; int Tree[210005],N; const int mx=200005; int a[100005],b[100005],L[100005],R[100005]; map<int,int> lst; int Lowbit(int x){ return (x&(-x));} int query(int x){int ans=0; for (int i=x;i;i-=Lowbit(i)) ans+=Tree[i];return ans;} void Insert(int x,int v){for (int i=x;i<=mx;i+=Lowbit(i)) Tree[i]+=v;} int main(){ scanf("%d",&N); for (int i=1;i<=N;i++){ scanf("%d",&a[i]); b[i]=a[i]; } sort(a+1,a+N+1); for (int i=1;i<=N;i++){ int x=b[i]; if (lst[b[i]]) b[i]=lst[b[i]]+1;else b[i]=lower_bound(a+1,a+N+1,b[i])-a; lst[x]=b[i]; } for (int i=1;i<=N;i++){ L[i]=query(b[i]-1); Insert(b[i],1); } memset(Tree,0,sizeof(Tree)); for (int i=N;i>=1;i--){ R[i]=query(b[i]-1); Insert(b[i],1); } int ans=0; for (int i=1;i<=N;i++){ ans+=min(L[i],R[i]); } cout<<ans; return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?