CSP提高组模拟1
我的微軟輸入法莫名其妙變成繁體了,你們有什麽頭緒嗎
狀態 | 題目 |
---|---|
20 Time Exceeded | A 最短路 |
25 Time Exceeded | B 方格取数 |
0 Time Exceeded | C 数组 |
70 Time Exceeded | D 树 |
A.最短路
我赛时想了想,会不会 DIJ 不是很对,因为这个题在打的时候觉得,在跑最短路的时候沿途加上点权有点草率了,但是后面没写完,所以也没回来想
有点像
我四十分的物理卷子
(内心):你做的这个对吗
我:管它呢,先画个圈,一会回来想,要不写不完了
(内心):你本来也写不完,要不再看看这题
(在想起一些不好的回忆
(因为太磨叽挂了五十分)之后,还是毅然地跳了)
(过了一会)
(内心):跳了这么多,你这不也没写完
我:我草我前面还有题没想
所以这个题还是用 Floyed,为啥要点权从小到大排序,其实这么一听觉得真是太对了,但是我懒得想了,所以还是三遍 Floyed 来得实在(只需要保证完全更新就行了)
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int inf=0x3f3f3f3f3f3f3f3f;
int mp[301][301];
int dis[301][301];
int w[301];
int n,m,q;
void floyd(){
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
int p=mp[i][k]+mp[k][j]+max(dis[i][k],dis[k][j]);
if((p<mp[i][j]+dis[i][j])&&mp[i][k]<inf&&mp[k][j]<inf){
mp[i][j]=mp[i][k]+mp[k][j];
dis[i][j]=max(dis[i][k],dis[k][j]);
}
}
}
}
}
signed main(){
freopen("path.in","r",stdin);
freopen("path.out","w",stdout);
cin>>n>>m>>q;
for(int i=1;i<=n;i++){
cin>>w[i];
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i==j) mp[i][j]=0;
else mp[i][j]=inf;
}
}
for(int i=1;i<=m;i++){
int x,y,z;
cin>>x>>y>>z;
if(mp[x][y]>z){
mp[x][y]=z;
mp[y][x]=z;
dis[x][y]=max(w[x],w[y]);
dis[y][x]=max(w[x],w[y]);
}
}
floyd();floyd();floyd();
for(int i=1;i<=q;i++){
int x,y;
cin>>x>>y;
int res=mp[x][y]+dis[x][y];
cout<<(res>=0x3f3f3f3f3f3f3f3f?-1:res)<<endl;
}
}
B. 方格取数
这个题的 \(n^4\) 挺好想的,我因为没什么头绪打的也是 \(n^4\) 暴力,没想到还能拿点分
如果有一个点大于等于 \(k\) 且小于等于 \(2k\) ,那么我们直接输出这个点即可
如果有一个点大于 \(2k\) ,那么我们一定不可以选这个点,和包括它的矩形
那么现在这个矩阵上只剩下不能选的点和权值小于 \(k\) 的点了,否则跑不到这里就输出答案了.
因此我们现在在这个简化的矩阵上考虑答案
我们可以考虑先找出一个最大的矩阵,即使它的权值大于 \(2k\),在我们一点点减少的过程中,因为所有节点都小于 \(k\),一定会在 \([k,2k]\) 内出现一次,因此直接大力删就行了. 删下来以后(其实就是切开了)要判一下哪边比较大(或者直接合法了,那更好)
洛谷上过了,咱们题库上 \(n\) 和 \(k\) 居然是反的,答案坐标也是反的 ,题库上莫名其妙 T 了, \(40\) 分,懒得改了,这并不是 AC 代码
#include<bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
#define N 2001
int k,n;
long long a[N][N];
long long sum[N][N];
int up[N][N],leftt[N],rightt[N];
long long ask(int lx,int ly,int ux,int uy){
return sum[ux][uy]-sum[lx-1][uy]-sum[ux][ly-1]+sum[lx-1][ly-1];
}
long long sumx;
int mat_sx,mat_sy,mat_ex,mat_ey;
void cutline(int x,int l,int r){
sumx=ask(x,l,x,r);
for(int i=r;i>=l;--i){
sumx-=a[x][i];
if(sumx>=k and sumx<=(k<<1)){
printf("%d %d %d %d",x,l,x,i-1);
exit(0);
}
}
}
void update(int lx,int ly,int ux,int uy){
long long t=ask(lx,ly,ux,uy);
if(t>=k and t<=(k<<1)){
printf("%d %d %d %d",lx,ly,ux,uy);
exit(0);
}
if(t>sumx){
sumx=t;
mat_sx=lx,mat_sy=ly,mat_ex=ux,mat_ey=uy;
}
}
stack<int>st;
signed main(){
freopen("matrix.in","r",stdin);
freopen("matrix.out","w",stdout);
cin>>n>>k;
for(int i=1;i<=n;++i){
for(int j=1;j<=n;++j){
cin>>a[i][j];
sum[i][j]=sum[i][j-1]+a[i][j];
if(a[i][j]>=k and a[i][j]<=(k<<1)){
printf("%d %d %d %d",i,j,i,j);
return 0;
}
}
for(int j=1;j<=n;++j){
sum[i][j]+=sum[i-1][j];
}
}
for(int i=1;i<=n;++i){
for(int j=1;j<=n;++j){
if(a[i][j]<k){
up[i][j]=up[i-1][j]+1;
}
}
while(!st.empty()) st.pop();
st.push(0);
up[i][0]=-1;
for(int j=1;j<=n;++j){
while(!st.empty() and up[i][st.top()]>=up[i][j]) st.pop();
leftt[j]=st.top()+1;
st.push(j);
}
while(!st.empty()) st.pop();
st.push(n+1);
up[i][n+1]=-1;
for(int j=n;j>=1;--j){
while(!st.empty() and up[i][st.top()]>=up[i][j]) st.pop();
rightt[j]=st.top()-1;
st.push(j);
if(up[i][j]) update(i-up[i][j]+1,leftt[j],i,rightt[j]);
}
}
if(sumx<k){
printf("-1\n");
return 0;
}
for(int i=mat_ex;i>=mat_sx;--i){
long long t=ask(i,mat_sy,i,mat_ey);
if(t>=k and t<=(k<<1)){
printf("%d %d %d %d",i,mat_sy,i,mat_ey);
return 0;
}
else if(t>(k<<1)){
cutline(i,mat_sy,mat_ey);
}
else{
sumx-=t;
if(sumx>=k and sumx<=(k<<1)){
printf("%d %d %d %d",mat_sx,mat_sy,i-1,mat_ey);
return 0;
}
}
}
}
C.数组
没想到搞了半天,线段树写对了,状压写对了,\(tag\) 写对了(其实是没写对,但是差不多对了),但是求 \(\phi\) 的时候寄了
🤣👉👉👉👉👉👉👉👉👉👉👉👉
这个题就是显然了,欧拉函数只要会求的就明白,直接根据积性函数(而且质因数也是差不多那种性质,所以极其好维护,取个或就行了)维护一下区间欧拉函数,扔到线段树里,拿出来的时候做个乘法就行了.
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int p=1000000007;
int power(int x,int t,int mod){
int ans=1,base=x;
while(t){
if(t&1){
ans=(ans*base)%mod;
}
base=base*base%mod;
t>>=1;
}
return ans;
}
struct tree{
int val;
int p;
int lazy,lazyP;
tree(){
val=1;
lazy=1;
p=0;
lazyP=0;
}
} t[1600001];
int n,q;
int a[1600001];
int f[1001],inv[1001];
int prime[]={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};
void update(int x){
t[x].val=t[x*2].val*t[x*2+1].val%p;
t[x].p=t[x*2].p|t[x*2+1].p;
}
void pushdown(int x,int l,int m,int r){
t[x*2].val=t[x*2].val*power(t[x].lazy,m-l+1,p)%p;
t[x*2+1].val=t[x*2+1].val*power(t[x].lazy,r-m,p)%p;
t[x*2].lazy=t[x*2].lazy*t[x].lazy%p;
t[x*2+1].lazy=t[x*2+1].lazy*t[x].lazy%p;
t[x*2].p|=t[x].lazyP;
t[x*2+1].p|=t[x].lazyP;
t[x*2].lazyP|=t[x].lazyP;
t[x*2+1].lazyP|=t[x].lazyP;
t[x].lazy=1;
t[x].lazyP=0;
}
void build(int l,int r,int x){
if(l==r){
t[x].val=a[l];
for(int i=0;i<62;++i){
if(a[l]%prime[i]==0){
t[x].p|=(1ll)<<i;
}
}
return;
}
int m=(l+r)/2;
build(l,m,x*2);
build(m+1,r,x*2+1);
update(x);
}
void update(int x,int l,int r,int delta,int prime,int cl,int cr){
if(cl>r or cr<l) return;
if(l<=cl and cr<=r){
t[x].val=t[x].val*power(delta,cr-cl+1,p)%p;
t[x].lazy=t[x].lazy*delta%p;
t[x].p|=prime;
t[x].lazyP|=prime;
return;
}
const int m=(cl+cr)/2;
pushdown(x,cl,m,cr);
update(x*2,l,r,delta,prime,cl,m);
update(x*2+1,l,r,delta,prime,m+1,cr);
update(x);
}
tree find(int x, int l, int r, int cl, int cr) {
if(cl>r or cr<l) return tree();
if(l<=cl and r>=cr) return t[x];
int m=(cl+cr)/2;
pushdown(x,cl,m,cr);
tree n;
tree ll=find(x*2,l,r,cl,m);
tree rr=find(x*2+1,l,r,m+1,cr);
n.val=ll.val*rr.val%p;
n.p=ll.p|rr.p;
return n;
}
signed main(){
freopen("array.in","r",stdin);
freopen("array.out","w",stdout);
cin>>n>>q;
for(int i=1;i<=n;++i){
cin>>a[i];
}
build(1,n,1);
inv[1]=1;
for(int i=2;i<=300;++i){
inv[i]=p-(p/i)*inv[p%i]%p;
}
for(int i=0;i<62;++i){
f[i]=inv[prime[i]]*(prime[i]-1)%p;
}
for(int i=1;i<=q;i++){
int opt,x,y;
cin>>opt>>x>>y;
if(opt==1){
int k;
cin>>k;
int t=0;
for(int i=0;i<62;++i){
if(k%prime[i]==0){
t|=(1ll)<<i;
}
}
update(1,x,y,k,t,1,n);
}
else{
tree ans=find(1,x,y,1,n);
int phi=ans.val;
for(int i=0;i<62;++i){
if(ans.p>>i&1){
phi=phi*f[i]%p;
}
}
cout<<phi%p<<endl;
}
}
}
D.树
懒得喷,谁造了组链的数据,还给那么大分
随机数据的话,考虑到先求个 Lca,然后两个节点都往 Lca 跳,每次都跳步长,一直到跳不动了,我们可以发现此时无论两个点位于哪里,中间一定不会再有其他点了.
因此倍增优化下 Lca,正好也能用来二进制优化跳步长,就能拿到这道题的大部分分.
啥比链,不写,感谢 luobotianle 提供的只过了链的假代码,拼上就行了
UPD: 原來链的不用根号分治也能过,这数据也太水了
#include<bits/stdc++.h>
using namespace std;
int n,w[50001];
vector<int>e[50001];
int fa[17][50001],deep[50001];
int xx[50001],yy[50001];
void dfs(int now,int last,int nowdeep){
fa[0][now]=last;
deep[now]=nowdeep;
for(int i:e[now]){
if(i!=last) dfs(i,now,nowdeep+1);
}
}
void prework(){
for(int i=1;i<=16;++i){
for(int j=1;j<=n;++j){
fa[i][j]=fa[i-1][fa[i-1][j]];
}
}
}
int jumpto(int x,int step){
int st=0;
while(step){
if(step&1){
x=fa[st][x];
}
step>>=1;
st++;
}
return x;
}
int lca(int x,int y){
if(deep[x]<deep[y]) swap(x,y);
for(int i=16;i>=0;--i){
// cout<<"deep "<<deep[x]<<" "<<deep[y]<<" "<<(1<<i)<<endl;
if(deep[x]-deep[y]>=(1<<i)){
// cout<<"jump "<<i<<endl;
x=fa[i][x];
}
}
for(int i=16;i>=0;--i){
if(fa[i][x]!=fa[i][y]){
x=fa[i][x];
y=fa[i][y];
}
}
// cout<<deep[x]<<" = "<<deep[y]<<endl;
// while(deep[x]!=deep[y]) x=fa[0][x];
// int cnt=0;
// while(x!=y){
// cnt++;
// x=fa[0][x],y=fa[0][y];
// }
// cout<<"jumpcnt "<<cnt<<endl;
// return x;
return x==y?x:fa[0][x];
}
int not_lca(int x,int y,int steplen){
int ans=w[x]+w[y],root=lca(x,y);
// cout<<"root "<<root<<endl;
// cout<<"++ "<<x<<" "<<y<<endl;
while(deep[x]-deep[root]>=steplen){
x=jumpto(x,steplen);
ans+=w[x];
// cout<<"+ "<<x<<endl;
}
while(deep[y]-deep[root]>=steplen){
y=jumpto(y,steplen);
// if(y!=root){
ans+=w[y];
// cout<<"+ "<<y<<endl;
// }
}
if(x==y) ans-=w[y];
return ans;
}
namespace lian{
const int N=5e4+5;
int a[N],b[N],c[N],x,y;
struct edge{
int next,to;
}e[N*2];
int h[N],cnt;
void add(int u,int v){
e[++cnt]={h[u],v};
h[u]=cnt;
}
int fa[N],top[N],son[N],dep[N],siz[N];
void dfs1(int x,int f){
fa[x]=f;
dep[x]=dep[f]+1;
siz[x]=1;
int maxc=0;
for(int i=h[x];i;i=e[i].next){
int to=e[i].to;
if(to==f)continue;
dfs1(to,x);
siz[x]+=siz[to];
if(siz[to]>maxc){
maxc=siz[to];
son[x]=to;
}
}
}
void dfs2(int x,int topf){
top[x]=topf;
if(!son[x])return;
dfs2(son[x],topf);
for(int i=h[x];i;i=e[i].next){
int to=e[i].to;
if(to==fa[x]||to==son[x])continue;
dfs2(to,to);
}
}
int lca(int x,int y){
while(top[x]!=top[y]){
// cout<<dep[top[x]]<<" "<<dep[top[y]]<<"\n";
if(dep[top[x]]<dep[top[y]])swap(x,y);
x=fa[top[x]];
}
return x;
}
void solve1(){
dfs2(1,1);dfs2(1,1);
for(int i=1;i<=n;i++)cin>>b[i];
for(int i=2;i<=n;i++)cin>>c[i];
for(int i=2;i<=n;i++){
int st=b[i-1],ed=b[i];
if(st>ed)swap(st,ed);
int ans=0;
for(int j=st;j<=ed;j+=c[i]){
ans+=a[j];
}
cout<<ans<<"\n";
}
}
int main(){
for(int i=1;i<=n;++i){
a[i]=w[i];
}
for(int i=1;i<n;i++){
add(xx[i],yy[i]);
add(yy[i],xx[i]);
}
solve1();
return 0;
dfs1(1,1);dfs2(1,1);
// for(int i=1;i<=n;i++){
// cout<<fa[i]<<" "<<dep[i]<<"\n";
// }
for(int i=1;i<=n;i++)cin>>b[i];
for(int i=2;i<=n;i++)cin>>c[i];
for(int i=2;i<=n;i++){
int st=b[i-1],ed=b[i];
int t=c[i];
int f=lca(st,ed);
int ans=0;
if(f==st||f==ed){
if(dep[st]<dep[ed])swap(st,ed);
ans=a[st];
while(st!=ed){
for(int j=1;j<=t;j++)st=fa[st];
ans+=a[st];
}
cout<<ans<<"\n";
}
else{
ans=a[st]+a[ed];
while(dep[st]-dep[f]>=t){
for(int j=1;j<=t;j++)st=fa[st];
ans+=a[st];
}
while(dep[ed]-dep[f]>=t){
for(int j=1;j<=t;j++)ed=fa[ed];
ans+=a[ed];
}
if(st==f&&ed==f)ans-=a[f];
cout<<ans<<"\n";
}
}
return 0;
}
}
int b[50001],c[50001];
int main(){
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
cin>>n;
for(int i=1;i<=n;++i){
cin>>w[i];
}
bool islian=true;
for(int i=1;i<=n-1;++i){
cin>>xx[i]>>yy[i];
if(abs(xx[i]-yy[i])!=1) islian=false;
e[xx[i]].push_back(yy[i]);
e[yy[i]].push_back(xx[i]);
}
if(islian){
lian::main();
}
else{
dfs(1,0,1);
prework();
for(int i=1;i<=n;++i){
cin>>b[i];
}
for(int i=1;i<=n-1;++i){
cin>>c[i];
}
for(int i=1;i<=n-1;++i){
cout<<not_lca(b[i],b[i+1],c[i])<<endl;
}
}
}