【比赛】CSP提高组模拟1

和初三学长们一起打的比赛,被人家爆杀了。
T1 最短路 20Pts
原题 Cow Toll Paths G。
正解是按点权排序后跑一遍 Floyd,歪解是多迭代几遍。
赛时没开 long long \(80 \to 20\)。
点击查看代码
#include<bits/stdc++.h>
#define int ll
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=305;
pii a[N][N];
int n,m,v[N],x,y,q,s,t,w;
main(){
freopen("path.in","r",stdin);
freopen("path.out","w",stdout);
cin>>n>>m>>q;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i==j)a[i][j]={0,0};
else a[i][j]={0x3f3f3f3f3f3f3f3f,0};
}
}
for(int i=1;i<=n;i++)cin>>v[i];
for(int i=1;i<=m;i++){
cin>>x>>y>>w;
w=min(w,a[x][y].first);
a[x][y].first=a[y][x].first=w;
a[x][y].second=a[y][x].second=max(v[x],v[y]);
}
int T=3;
while(T--)
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
if(i==k)continue;
for(int j=1;j<=n;j++){
if(j==k||i==j)continue;
if(a[i][j].first+a[i][j].second>a[i][k].first+a[k][j].first+max(a[i][k].second,a[k][j].second)){
a[i][j].first=a[i][k].first+a[k][j].first;
a[i][j].second=max(a[i][k].second,a[k][j].second);
}
}
}
}
while(q--){
cin>>x>>y;
if(a[x][y].first==0x3f3f3f3f3f3f3f3f)cout<<"-1\n";
else cout<<a[x][y].first+a[x][y].second<<"\n";
}
return 0;
}
T2 方格取数 40Pts
原题 KUP-Plot purchase。
首先判掉在 \([k,2k]\)(直接输出) 和 \((2k,+\infty)\)(一定不选)的数,剩下的数就都在 \((1,k-1)\) 之间了;
此时如果我们能找出一个总和大于 \(2k\) 的矩阵,那么一定有解,因为其一定包括了一个总和在 \([k,2k]\) 之间的矩阵。
所以我们先找出一个总和大于 \(2k\) 的极大矩阵,然后每次删去一行,如果这一行的和大于 \(k\) 就在这一行中一个一个删,否则继续在大矩阵中进行。
单调栈找最大矩阵即可。
赛时想到了单调栈,但是没打出来,随机化 40。
点击查看代码
#include<bits/stdc++.h>
#define int ll
using namespace std;
typedef long long ll;
const int N=2005;
int n,k,a[N][N],sum[N][N],l[N][N],r[N][N],s[N][N],mp[N][N];
int get(int x,int y,int xx,int yy){
return sum[xx][yy]-sum[x-1][yy]-sum[xx][y-1]+sum[x-1][y-1];
}
main(){
freopen("matrix.in","r",stdin);
freopen("matrix.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n>>k;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cin>>a[i][j];
if(a[i][j]>=k&&a[i][j]<=k*2){
cout<<i<<" "<<j<<" "<<i<<" "<<j;
return 0;
}
else mp[i][j]=(a[i][j]<k);
sum[i][j]=a[i][j]+sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(mp[i][j])s[i][j]=s[i-1][j]+1;
else s[i][j]=0;
}
}
for(int i=1;i<=n;i++){
stack<int> ss;
for(int j=1;j<=n+1;j++){
while(!ss.empty()&&s[i][j]<s[i][ss.top()]){
r[i][ss.top()]=j-1;
ss.pop();
}
ss.push(j);
}
while(!ss.empty())ss.pop();
for(int j=n;j>=0;j--){
while(!ss.empty()&&s[i][j]<s[i][ss.top()]){
l[i][ss.top()]=j+1;
ss.pop();
}
ss.push(j);
}
for(int j=1;j<=n;j++){
int now=get(i-s[i][j]+1,l[i][j],i,r[i][j]);
if(now>=k){
if(now<=k*2){
cout<<i-s[i][j]+1<<" "<<l[i][j]<<" "<<i<<" "<<r[i][j];
return 0;
}
else{
for(int h=l[i][j]+1;h<=r[i][j];h++){
int noww=sum[i][h]-sum[i][l[i][j]-1];
if(noww>=k&&noww<=2*k){
cout<<i<<" "<<l[i][j]<<" "<<i<<" "<<h;
return 0;
}
}
}
}
}
}
cout<<-1;
return 0;
}
T3 数组 0Pts
原题 Please, another Queries on Array?。
对于欧拉函数,有式子 \(\varphi(x)=x \ \cdot \ \prod_{i=1}^n\limits (\frac{p_i-1}{p_i})\),其中 \(p_i\) 为 \(x\) 的所有质因数。
区间积可以用线段树维护,难点在于质因子的维护。
通过一些技术手段,我们发现,在 300 以内只有 62 个质因子,刚好可以用一个 long long 来状压。
同样丢到线段树里即可。
赛时因为 1<<62 爆零了。
1<<62 \(\to\) 1ll<<62 \(0 \to 100\)
点击查看代码
#include<bits/stdc++.h>
#define int ll
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int mod=1e9+7;
const int N=4e5+5;
int n,a[N],l,r,x,q;
string op;
int prim[]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293};
inline int qp(int a,int b,int mod=mod){
int ans=1;
while(b){
if(b&1)ans=ans*a%mod;
b>>=1;
a=a*a%mod;
}
return ans;
}
inline int getp(int x){
int p=0;
for(int i=0;i<62;i++){
if(x%prim[i]==0)p|=(1ll<<i);
}
return p;
}
struct tree{
int l,r,mul,lzm;
int p,lzp;
int len(){return r-l+1;}
}t[N<<2];
void pushup(int k){
t[k].mul=t[k<<1].mul*t[k<<1|1].mul%mod;
t[k].p=t[k<<1].p|t[k<<1|1].p;
}
void build(int k,int l,int r){
t[k]={l,r,a[l],1};
if(l==r){
int p=getp(a[l]);
t[k].p=p;t[k].lzp=0;
return;
}
int mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
pushup(k);
}
void pushdown(int k){
if(t[k].lzm!=1){
t[k<<1].mul=t[k<<1].mul*qp(t[k].lzm,t[k<<1].len())%mod;
t[k<<1].lzm=t[k<<1].lzm*t[k].lzm%mod;
t[k<<1|1].mul=t[k<<1|1].mul*qp(t[k].lzm,t[k<<1|1].len())%mod;
t[k<<1|1].lzm=t[k<<1|1].lzm*t[k].lzm%mod;
t[k].lzm=1;
}
if(t[k].lzp!=0){
t[k<<1].p|=t[k].lzp;
t[k<<1].lzp|=t[k].lzp;
t[k<<1|1].p|=t[k].lzp;
t[k<<1|1].lzp|=t[k].lzp;
t[k].lzp=0;
}
}
void update(int k,int l,int r,int val,int p){
if(l<=t[k].l&&t[k].r<=r){
t[k].mul=t[k].mul*qp(val,t[k].len())%mod;
t[k].lzm=t[k].lzm*val%mod;
t[k].p|=p;
t[k].lzp|=p;
return;
}
pushdown(k);
int mid=(t[k].l+t[k].r)>>1;
if(l<=mid)update(k<<1,l,r,val,p);
if(r>mid)update(k<<1|1,l,r,val,p);
pushup(k);
}
pii query(int k,int l,int r){
if(l<=t[k].l&&t[k].r<=r){
return {t[k].mul,t[k].p};
}
pushdown(k);
int mid=(t[k].l+t[k].r)>>1;
pii ans={1,0};
if(l<=mid){
pii L=query(k<<1,l,r);
ans.first=ans.first*L.first%mod;
ans.second|=L.second;
}
if(r>mid){
pii R=query(k<<1|1,l,r);
ans.first=ans.first*R.first%mod;
ans.second|=R.second;
}
return ans;
}
int inv[500];
main(){
freopen("array.in","r",stdin);
freopen("array.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
for(int i=1;i<=400;i++)inv[i]=qp(i,mod-2);
cin>>n>>q;
for(int i=1;i<=n;i++)cin>>a[i];
build(1,1,n);
while(q--){
cin>>op;
if(op=="1"){
cin>>l>>r>>x;
int p=getp(x);
update(1,l,r,x,p);
}
else{
cin>>l>>r;
pii tmp=query(1,l,r);
int ans=tmp.first,p=tmp.second;
for(int i=0;i<62;i++){
if((p>>i)&1){
ans=ans*(prim[i]-1)%mod*inv[prim[i]]%mod;
}
}
cout<<ans<<"\n";
}
}
return 0;
}
T4 树 30Pts
原题 ODW。
根号分治。
设块长为 \(T\)。
预处理出步长为 \([1,\sqrt T]\) 时每个点向上跳到根的点权和以及每个点的 \(1 \backsim \sqrt T\)
大于 \(T\) 则暴力跳,否则树上差分。
这题数据很水,暴力有 70,能过除了链以外的所有点;加个对链的暴力就过了。
但赛时树剖 LCA 打锅了导致 \(100 \to 30\)
后记
还是学到了很多的,也确实是技不如人。
之后要注意代码细节问题,尽量不要在不该挂的地方挂分。