考试复现
8:00~8:15 看题。
8:15~8:40 写T1。
8:40~10:04 写T2,画的时间有点长,想到过正解的思路,但是无法证明是否正确,就没有继续想。
10:04~12:00 由于T3的题面太长,并且看不懂,所以写T4。
A. 数论入门
起初的思路是先预处理处k以内的所有质数,再枚举是k的因子的质数在k中的个数,答案时所有质因子的个数除以二向上取整次方的乘积。但是五星数学家麦克阿瑟曾经说过:“我花了一晚上用欧拉筛求1e12以内质数,不是我求出来了,而是天亮了。”会超时。所以转变我方向,如果k是平方数的话,那么答案就是,否则答案肯定是大于
且小于k的一个数,那么我就可以从
到2枚举(2<=i<=
),看k可以除几个(k/=i),num(初值为1)就乘几个i,若num=1,输出-1,否则输出k/num;
#include<bits/stdc++.h>
using namespace std;
long long n,ans=1;
int main()
{
scanf("%lld",&n);
long long num=n;
for(long long i=sqrt(n);i>=2;--i)
{
while(num%(i*i)==0)
{
ans*=i;
num/=i*i;
}
}
if(ans==1) cout<<-1<<endl;
else cout<<n/ans<<endl;
return 0;
}
B. 删库跑路
前20分码暴力,复杂度O(),枚举n个点的删除顺序。
后30分=1,用单调队列存每个点的入度,入度小的先删除,入度大的后删除。
正解:
考虑一条边被删除当且仅当这条边两端点之一被删除。假设这条边连接x、y,那么这条边带来的贡献要么是删x带来,要么是删y带来
。也就是说,
一定是答案的一个下界,答案不可能比它小。考虑能否构造出一个删除方案使得答案等于它。给每一条无向边赋方向:让点权大的点指向点权小的点。若两点点权相同,让编号大的点指向编号小的点。这样构造出的有向图一定是一个DAG。跑出来的拓扑序就是删点的顺序。所以,证明了一定可以构造出一个删点方案,使得
。在读入的时候加一下即可。
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int head[N],last[N*4],to[N*4],tot;
int n,m,x,y,du[N],vs[N];
long long v[N],num[N],ans;
void add(int x,int y)
{
to[++tot]=y;
last[tot]=head[x];
head[x]=tot;
}
priority_queue<pair<int,int> >q;
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i) scanf("%lld",&v[i]);
for(int i=1;i<=m;++i)
{
scanf("%d%d",&x,&y);
if(v[x]>v[y]||v[x]==v[y]&&x<y) add(x,y),du[y]++;
else add(y,x),du[x]++;
num[x]+=v[y],num[y]+=v[x];
}
for(int i=1;i<=n;++i) q.push(make_pair(-du[i],i));
int t=0;
while(t<n)
{
int id=q.top().second;
q.pop();
t++;
ans+=num[id];
vs[id]=1;
for(int i=head[id];i;i=last[i])
{
int y=to[i];
if(vs[y]) continue;
num[y]-=v[id];
du[y]--;
q.push(make_pair(-du[y],y));
}
}
printf("%lld\n",ans);
return 0;
}
C. -1+i进制系统
考试的时候没写,原因其一是题目太长了,看晕了,其二是虚数这一块每看明白,不过同学一讲后就明白了,这不就是个数学题吗,简单的不行。
正解:
把公式拆解后写个dfs就行了。
#include<bits/stdc++.h>
using namespace std;
#define int long long
string s;
int f1=1,f2=1,a,b;
bool num=false;
stack<int> ans;
void dfs(int a,int b)
{
if(a==0&&b==0) return;
int r=abs(a-b)%2;
ans.push(r);
dfs((b-a+r)/2,(-b-a+r)/2);
}
signed main()
{
cin>>s;
int po=1,ff=1,tot=-1;
for(int i=s.size()-1;i>=0;--i)
{
if(s[i]=='i')
{
ff=0;
continue;
}
if(ff) continue;
if(s[i]=='-')
{
f2=-1;
tot=i-1;
break;
}
else if(s[i]=='+')
{
tot=i-1;
break;
}
b+=(s[i]-'0')*po;
po*=10;
}
if(ff) tot=s.size()-1;
for(int i=0;i<=tot;++i)
{
if(s[i]=='-') f1=-1;
else a=a*10+s[i]-'0';
}
a*=f1,b*=f2;
dfs(a,b);
while(!ans.empty())
{
cout<<ans.top();
ans.pop();
}
return 0;
}
D. 醉醉疯疯渺渺空空
不妨令1为根,则如果x和y的lca并非xy之一,则比较好实现,维护f[x]表示x到它的儿子们的价值之和,,f,y是x的儿子,d[x]表示x的深度,则答案为
。不妨设x是y的祖先,则我们可以维护g[x]表示x的父亲,距离除了x之外的子树的点和祖先的价值之和,换根dp维护之即可。我们先跑一遍dfs预处理出f[x],再跑第二遍时,不妨假设x的父亲已经算好了,则x的g[x]为
,这些可以递归地进行。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e6+5,mod=998244353;
int read()
{
int num=0;
bool flag=1;
char c=getchar();
for(;c<'0'||c>'9';c=getchar())
if(c=='-')flag=0;
for(;c>='0'&&c<='9';c=getchar())
num=(num<<1)+(num<<3)+c-'0';
return flag?num:-num;
}
int head[N],last[N<<1],to[N<<1],tot;
void add(int x,int y)
{
to[++tot]=y;
last[tot]=head[x];
head[x]=tot;
}
int n,m,x,y,f[N],ff[N][25],deep[N],prime[N];
void get_way(int x,int fat)
{
f[x]=1;
deep[x]=deep[fat]+1;
for(int i=0;i<=19;i++) ff[x][i+1]=ff[ff[x][i]][i];
for(int i=head[x];i;i=last[i])
{
int y=to[i];
if(y==fat) continue;
ff[y][0]=x;
get_way(y,x);
f[x]=(f[x]+f[y]*2%mod)%mod;
}
}
int LCA(int x,int y)
{
if(deep[y]>deep[x]) swap(x,y);
for(int i=20;i>=0;i--)
{
if(deep[ff[x][i]]>=deep[y]) x=ff[x][i];
if(x==y) return x;
}
for(int i=20;i>=0;i--)
{
if(ff[x][i]!=ff[y][i])
{
x=ff[x][i];
y=ff[y][i];
}
}
return ff[x][0];
}
int g[N];
void get_g(int x,int nn)
{
g[x]=nn;
for(int i=head[x];i;i=last[i])
{
int y=to[i];
if(ff[x][0]==y) continue;
get_g(y,(g[x]*2%mod+(f[x]-f[y]*2%mod+mod)%mod*2%mod)%mod);
}
}
int get_fa(int y,int x)
{
for(int i=20;i>=0;--i)
if(deep[ff[y][i]]>=x) y=ff[y][i];
return y;
}
signed main()
{
n=read();
for(int i=1;i<n;++i)
{
x=read(),y=read();
add(x,y),add(y,x);
}
prime[0]=1;
for(int i=1;i<=n;++i) prime[i]=prime[i-1]*2%mod;
m=read();
get_way(1,0);
get_g(1,0);
for(int i=1;i<=m;++i)
{
x=read(),y=read();
int lca=LCA(x,y);
if(lca!=x&&lca!=y) printf("%lld\n",prime[deep[x]+deep[y]-2*deep[lca]]*f[x]%mod*f[y]%mod);
else
{
if(deep[x]<deep[y]) swap(x,y);
int xx=get_fa(x,deep[y]+1);
printf("%lld\n",((g[y]+f[y]-2*f[xx])%mod+mod)%mod*f[x]%mod*prime[deep[x]-deep[y]]%mod);
}
}
return 0;
}