10.4 模拟赛
前言
业精于勤荒于嬉,行成于思毁于随
正文(模拟赛)
卦象:凶
感受:没什么感受,半个小时码完 T1,三个小时码完 T2,剩下半个小时码完 T3 的暴力,获得 220pts,并没有挂分
T1
小分讨题
点击查看代码
#include<bits/stdc++.h>
using namespace std;
string s;int n;
inline int cal(string s){
if(s[0]==s[1]&&s[1]==s[2])return 1;
if(s[0]!=s[1]&&s[1]!=s[2]&&s[2]!=s[0])return 3;
return 2;
}
inline void solve(){
cin>>s>>n;
if(n==0){cout<<1<<'\n';return;}
int cnt=cal(s);
if(cnt==1)cout<<1<<'\n';
else if(cnt==2)cout<<(n==1?7:8)<<'\n';
else if(cnt==3)cout<<(n==1?24:27)<<'\n';
return;
}
int main(){
freopen("device.in","r",stdin);
freopen("device.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int T;cin>>T;while(T--)solve();
return 0;
}
T2
大分讨题
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=4,V=20091023;
int a[N],b[N],d[N],q[N],t[N],tmp[N],c[N];bool vis[N];
inline void check(){
int d1=V,d2=V;bool flag=true;
for(int i=1;i<=3;i++){
if(vis[i])continue;
if(b[i])flag=false;
if(d1==V)d1=b[i]-a[i];
else d2=b[i]-a[i];
}
if(flag){cout<<1<<'\n';return;}
if(d1==d2){cout<<1<<'\n';return;}
int q1=V,q2=V;
for(int i=1;i<=3;i++){
if(vis[i])continue;
if(a[i]==0||b[i]%a[i]!=0){cout<<2<<'\n';return;}
if(q1==V)q1=b[i]/a[i];
else q2=b[i]/a[i];
}
cout<<((q1==q2&&q1!=V)?1:2)<<'\n';
return;
}
inline bool work1(int x,int y,int z){
memcpy(c,a,sizeof(c));
c[x]+=d[y],c[y]+=d[y];
if(q[z]==V)return false;
c[x]*=q[z],c[z]*=q[z];
for(int i=1;i<=3;i++)
if(c[i]!=b[i])return false;
return true;
}
inline bool work2(int x,int y,int z){
memcpy(c,a,sizeof(c));
if(q[y]==V)return false;
c[x]*=q[y],c[y]*=q[y];
c[x]+=d[z],c[z]+=d[z];
for(int i=1;i<=3;i++)
if(c[i]!=b[i])return false;
return true;
}
inline bool work3(int x,int y,int z){
memcpy(c,b,sizeof(c));
if(q[z]==V||q[z]==0)return false;
if(c[x]%q[z]!=0||c[y]%q[z]!=0)return false;
c[x]/=q[z],c[y]/=q[z];
return c[x]-a[x]==c[y]-a[y];
}
inline bool work4(int x,int y,int z){
memcpy(c,b,sizeof(c));
c[x]-=d[z],c[y]-=d[z];
if(c[x]==0&&c[y]==0)return true;
if(a[x]==0){
if(c[x]!=0)return false;
if(a[y]==0)return false;
return c[y]%a[y]==0;
}
if(a[y]==0){
if(c[y]!=0)return false;
if(a[x]==0)return false;
return c[x]%a[x]==0;
}
if(c[x]%a[x]!=0||c[y]%a[y]!=0)return false;
return c[x]/a[x]==c[y]/a[y];
}
inline bool work5(int x,int y,int z){
memcpy(c,a,sizeof(c));
for(int i=1;i<=3;i++)c[i]+=d[z];
if(b[x]==0&&b[y]==0)return true;
if(c[x]==0){
if(b[x]!=0)return false;
if(c[y]==0)return false;
return b[y]%c[y]==0;
}
if(c[y]==0){
if(b[y]!=0)return false;
if(c[x]==0)return false;
return b[x]%c[x]==0;
}
if(b[x]%c[x]!=0||b[y]%c[y]!=0)return false;
return b[x]/c[x]==b[y]/c[y];
}
inline bool work6(int x,int y,int z){
memcpy(c,a,sizeof(c));
for(int i=1;i<=3;i++)c[i]*=q[z];
return b[x]-c[x]==b[y]-c[y];
}
inline bool work7(){
int p=b[2]-b[1],q=a[2]-a[1];
if(q==0)return false;
if(p%q!=0)return false;
int x=p/q,y=b[1]-a[1]*x;
return a[3]*x+y==b[3];
}
inline bool work8(){
int p=b[2]-b[1],q=a[2]-a[1];
if(q==0)return false;
if(p%q!=0)return false;
int y=p/q;
if(y==0)return false;
if(b[1]%y!=0)return false;
int x=b[1]/y-a[1];
return (a[3]+x)*y==b[3];
}
inline void solve(){
for(int i=1;i<=3;i++)cin>>a[i];
for(int i=1;i<=3;i++)cin>>b[i];
for(int i=1;i<=3;i++)vis[i]=(a[i]==b[i]);
int cnt=0;
for(int i=1;i<=3;i++)cnt+=(vis[i]==false);
if(cnt==0){cout<<0<<'\n';return;}
if(cnt==1){cout<<1<<'\n';return;}
if(cnt==2){check();return;}
for(int i=1;i<=3;i++)d[i]=q[i]=V;
// ans=1
for(int i=1;i<=3;i++)d[i]=b[i]-a[i];
if(d[1]==d[2]&&d[2]==d[3]){cout<<1<<'\n';return;}
// for(int i=1;i<=3;i++)cerr<<d[i]<<' ';
// cerr<<endl;
for(int i=1;i<=3;i++)
if(a[i]!=0&&b[i]%a[i]==0)q[i]=b[i]/a[i];
// for(int i=1;i<=3;i++)cerr<<q[i]<<' ';
// cerr<<endl;
if(q[1]==q[2]&&q[2]==q[3]&&q[1]!=V){cout<<1<<'\n';return;}
// ans=2
memcpy(t,d,sizeof(t));sort(t+1,t+4);
memcpy(tmp,q,sizeof(tmp));sort(tmp+1,tmp+4);
if(t[1]==t[2]||t[2]==t[3]){cout<<2<<'\n';return;}
if(t[1]+t[2]==t[3]||t[1]+t[3]==t[2]||t[2]+t[3]==t[1]){cout<<2<<'\n';return;}
if((tmp[1]==tmp[2]&&tmp[1]!=V)||(tmp[2]==tmp[3]&&tmp[2]!=V)){cout<<2<<'\n';return;}
if(tmp[1]*tmp[2]==tmp[3]||tmp[1]*tmp[3]==tmp[2]||tmp[2]*tmp[3]==tmp[1]){cout<<2<<'\n';return;}
for(int i=1;i<=3;i++)
for(int j=1;j<=3;j++){
if(i==j)continue;
if(work1(i,j,6-i-j)){cout<<2<<'\n';return;}
}
for(int i=1;i<=3;i++)
for(int j=1;j<=3;j++){
if(i==j)continue;
if(work2(i,j,6-i-j)){cout<<2<<'\n';return;}
}
for(int i=1;i<=3;i++)
for(int j=1;j<=3;j++){
if(i==j)continue;
if(work3(i,j,6-i-j)){cout<<2<<'\n';return;}
}
for(int i=1;i<=3;i++)
for(int j=1;j<=3;j++){
if(i==j)continue;
if(work4(i,j,6-i-j)){cout<<2<<'\n';return;}
}
for(int i=1;i<=3;i++)
for(int j=1;j<=3;j++){
if(i==j)continue;
if(work5(i,j,6-i-j)){cout<<2<<'\n';return;}
}
for(int i=1;i<=3;i++)
for(int j=1;j<=3;j++){
if(i==j)continue;
if(work6(i,j,6-i-j)){cout<<2<<'\n';return;}
}
if(work7()){cout<<2<<'\n';return;}
if(work8()){cout<<2<<'\n';return;}
// ans=3
cout<<3<<'\n';
return;
}
inline void clr(){
for(int i=1;i<=3;i++)d[i]=q[i]=V,vis[i]=false;
return;
}
signed main(){
freopen("triple.in","r",stdin);
freopen("triple.out","w",stdout);
int T;cin>>T;while(T--)solve(),clr();
return 0;
}
T3
两个做法,都是单根号的
法一
考虑对 \(m\) 根号分治,设阈值为 \(B\)
如果 \(m>B\),那么最多有 \(O(\frac{n}{B})\) 次这样的询问,容易做到单次 \(O(n)\),使得复杂度 \(O(\frac{n^2}{B})\)
如果 \(m \le B\),你发现 \(x_i\) 把序列划分为不超过 \(m\) 个连续段。精妙地是,你可以把所有的询问离线下来。枚举两个连续段,统计满足 \(L_x \le l_i \le R_x ,\ L_y \le r_i \le R_y\) 的区间 \([l_i,r_i]\) 个数,显然可以二维数点,区间的奇偶性是简单的
具体地,你可以对 \(l_i\) 进行扫描线,值域上维护 \(r_i \in [L,R]\) 的个数
你可以做到 \(O(n)\) 次修改,\(O(nB)\) 次查询;所以二维数点需要分块进行时间复杂度平衡,看起来不算特别难
点击查看代码
#include<bits/stdc++.h>
#define pii pair<int,int>
#define fi first
#define se second
#define mkp make_pair
#define vi vector<int>
#define vp vector<pii>
#define vn vector<NODE>
#define pb push_back
using namespace std;
const int N=5e5+5,B=710;
int n,Q,l[N],r[N],ans[N];vi X,p[N];
struct que{vp vec;int id;}q[N];int tol;
struct NODE{int o1,o2,o;};vn lmydj[N];
struct BLK{
int a[N],tag[N],blk[N],L[B],R[B],siz,tot;
inline void build(){
siz=sqrt(n),tot=n/siz;
for(int i=1;i<=tot;i++)L[i]=(i-1)*siz+1,R[i]=i*siz;
if(R[tot]<n)tot++,L[tot]=R[tot-1]+1,R[tot]=n;
for(int i=1;i<=tot;i++)for(int j=L[i];j<=R[i];j++)blk[j]=i;
return;
}
inline void add(int u){
for(int i=u;i<=R[blk[u]];i++)a[i]++;
for(int i=blk[u]+1;i<=tot;i++)tag[i]++;
return;
}
inline int ask(int u){return a[u]+tag[blk[u]];}
inline int qry(int l,int r){return ask(r)-ask(l-1);}
}K;
int main(){
freopen("badge.in","r",stdin);
freopen("badge.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>Q;
for(int i=1;i<=n;i++){
cin>>l[i]>>r[i];
p[r[i]].pb(l[i]);
}
for(int k=1;k<=Q;k++){
int m;cin>>m;X.clear();
for(int i=1,x;i<=m;i++)cin>>x,X.pb(x);
sort(X.begin(),X.end());
if(m>B){
int a[N]={},sum[N]={};
for(int i=0;i<m;i++)a[X[i]]++;
for(int i=1;i<=n;i++)sum[i]=sum[i-1]+a[i];
int res=0;
for(int i=1;i<=n;i++)res+=((sum[r[i]]-sum[l[i]-1])%2);
ans[k]=res;
}else{
tol++;int pos=0;
for(int i=0;i<m;i++)q[tol].vec.pb(mkp(pos,X[i])),pos=X[i];
q[tol].vec.pb(mkp(X.back(),n+1));q[tol].id=k;
}
}
for(int i=1;i<=tol;i++){
int _size=q[i].vec.size();
for(int j=0;j<_size;j++){
if(q[i].vec[j].fi>0)lmydj[q[i].vec[j].fi-1].pb({i,j,-1});
lmydj[q[i].vec[j].se-1].pb({i,j,1});
}
}
K.build();
for(int i=1;i<=n;i++){
for(int x:p[i])K.add(x);
for(auto x:lmydj[i]){
int idx=x.o1,cur=x.o2;
for(int k=0;k<cur;k++){
if(!((cur-k)&1))continue;
ans[q[idx].id]+=x.o*K.qry(q[idx].vec[k].fi+1,q[idx].vec[k].se);
}
}
}
for(int i=1;i<=Q;i++)cout<<ans[i]<<'\n';
return 0;
}
法二
咱们还有传说中的莫队做法
用莫队维护一个长度为 \(Q\) 的 \(01\) 序列,表示在某时间戳上该区间是否可以贡献答案
然后我们把修改放进来,区间扩展相当于做一个单点 flip
,而答案相当于求历史和,在扩展的同时进行维护
然后就没了
代码短,但是跑不过前面的根号分治
点击查看代码
#include<bits/stdc++.h>
#define pii pair<int,int>
#define fi first
#define se second
#define mkp make_pair
#define vi vector<int>
#define vp vector<pii>
#define pb push_back
#define lwbd lower_bound
#define upbd upper_bound
using namespace std;
const int N=5e5+5;
int n,Q,b[N],tol;pii a[N];
struct que{
int l,r,blk;
friend bool operator < (que s,que t){
if(s.blk!=t.blk)return s.l<t.l;
return ((s.blk&1)?s.r<t.r:s.r>t.r);
}
}q[N];
bool vis[N];int ans[N];
inline void add(int x,int tim){
if(vis[x])ans[x]+=tim-1,vis[x]=false;
else ans[x]-=tim-1,vis[x]=true;
return;
}
int main(){
freopen("badge.in","r",stdin);
freopen("badge.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>Q;int siz=sqrt(n);
for(int i=1;i<=n;i++)cin>>q[i].l>>q[i].r;
for(int k=1;k<=Q;k++){
int m;cin>>m;
for(int i=1;i<=m;i++){
int x;cin>>x;
a[++tol]=mkp(x,k);b[tol]=x;
}
}
a[++tol]=mkp(0,0);b[tol]=0;
a[++tol]=mkp(n+1,0);b[tol]=n+1;
sort(a+1,a+tol+1);sort(b+1,b+tol+1);
for(int i=1;i<=n;i++)q[i].l=lwbd(b+1,b+tol+1,q[i].l)-b;
for(int i=1;i<=n;i++)q[i].r=upbd(b+1,b+tol+1,q[i].r)-b-1;
for(int i=1;i<=n;i++)q[i].blk=(q[i].l-1)/siz+1;
sort(q+1,q+n+1);
int l=1,r=0;
for(int i=1;i<=n;i++){
int ql=q[i].l,qr=q[i].r;
while(r<qr)add(a[++r].se,i);
while(l>ql)add(a[--l].se,i);
while(r>qr)add(a[r--].se,i);
while(l<ql)add(a[l++].se,i);
}
for(int i=1;i<=Q;i++)
if(vis[i])ans[i]+=n;
for(int i=1;i<=Q;i++)cout<<ans[i]<<'\n';
return 0;
}
T4
两个做法,代码短与时间快
法一
约定:\(n\) 表示原题中的 \(n-1\),\(m\) 表示原题中的 \(m-1\)
感觉 \(O(nm)\) 是相当好做的,相当于直接枚举一个向量,然后在平面内漂移,统计其出现次数
我们保留其中一部分思路,即依旧考虑枚举边
记 \(f(i)\) 表示一条线段上有 \(x+1\) 的整点的边数,计算答案可以容斥
具体地,枚举边,枚举边外一个点,再刨除三点共线的情况。形式化地,有
第一项是这样的,除了 \(+2\) 都是好理解的,那个 \(+2\) 是当第三个点漂移到与当前枚举边的两个端点时,我们少计算一次整点的贡献
而后面的部分也很简单,三点共线本质就是最长线段包含整点个数 \(\times 2\)
当然,上述式子可以化简,写成下面的形式
问题转化为如何快速求 \(f(i)\)
可以发现,整点个数恰为 \(i+1\) 的线段需要满足 \(\gcd(\Delta x , \Delta y)=i\),所以不妨枚举 \(i\) 的倍数 \(x\),求 \(g(i)=\sum \limits _{i \mid x} f(x)\)
显然求 \(g\) 可以差分求出 \(f\),并且 \(g\) 是好算的,考虑其几何意义,有:
哦,然后直接维护维护完了
点击查看代码
#include<bits/stdc++.h>
#define int __int128
using namespace std;
const int N=1e6+5;
int n,m,f[N];
inline int read(){
int x=0;char c=getchar();
while(c<'0'||c>'9')c=getchar();
while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();
return x;
}
inline void write(int x){
if(x>9)write(x/10);
putchar(x%10+'0');
return;
}
inline void solve(){
n=read(),m=read();n--,m--;
int ans=0;
memset(f,0,sizeof(f));
for(int i=max(n,m);i>=1;i--){
int sum1=0,sum2=0;
for(int j=1;j<=n/i;j++)sum1+=(n-i*j+1);
for(int j=1;j<=m/i;j++)sum2+=(m-i*j+1);
f[i]=2*sum1*sum2;
for(int j=1;j<=n/i;j++)f[i]+=(n-i*j+1)*(m+1);
for(int j=1;j<=m/i;j++)f[i]+=(m-i*j+1)*(n+1);
for(int j=2*i;j<=max(n,m);j+=i)f[i]-=f[j];
ans+=f[i]*((n+1)*(m+1)-2*i)*i;
}
write(ans);puts("");
return;
}
signed main(){
freopen("grid.in","r",stdin);
freopen("grid.out","w",stdout);
int T=read();while(T--)solve();
return 0;
}
法二
指路 HTC 大巨的题解
小结
下午效率好低……
后记
世界孤立我任它奚落
完结撒花!