今天突然( m pdf)题面了,认真打了打,于是自闭了
( m T1)是个简单的结论题,给定(n,m,q),求([1,q])里不能被(n imes x+m imes y)表示的数有多少个
一眼大凯的疑惑既视感
首先设(r=(n,m)),能表示的数就一定是(r)的倍数,于是我们让(n=frac{n}{r},m=frac{m}{r}),求一下([1,left lfloor frac{q}{r} ight floor])内能被表示的数即可,让(q)减去这个数就是答案了
注意到现在(nperp m),考虑小凯的疑惑中的结论,不能被表示的最大数是(n imes m-n-m),即一个数(c)能被表示,必定唯一存在(c=n imes x+m imes y),且(x<m,y<n)
注意到(nleq 10^5,y<n),于是考虑枚举这个(y),于是在([m imes y,left lfloor frac{q}{r} ight floor])范围内所有(n)的倍数都能被表示出来,这样的数的个数显然有(left lfloor frac{(left lfloor frac{q}{r} ight floor-m imes y)}{n} ight floor +1)个
最后注意到(0)也被视为能表示的数了,减掉即可
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define re register
#define LL long long
#define min(a,b) ((a)<(b)?(a):(b))
inline int gcd(int a,int b) {
return !b?a:gcd(b,a%b);
}
int T,n,m;
LL q;
int main() {
freopen("simple.in","r",stdin);
freopen("simple.out","w",stdout);
scanf("%d",&T);
while(T--) {
scanf("%d%d%lld",&n,&m,&q);
if(n>m) std::swap(n,m);
int r=gcd(n,m);LL ans=q;
n/=r,m/=r,q/=r;
for(re int i=0;i<n&&1ll*m*i<=q;++i)
ans-=(q-1ll*m*i)/n,ans--;
printf("%lld
",ans+1);
}
return 0;
}
由于在最后二十分钟才推出结论而过于兴奋,让(q)对(n imes m-n-m)取了一个(min),而(ans)的初值设成了(min)之后的(q),于是获得了(10 m pts)的好成绩
( m T2)给定一棵树,边有边权,长度都为(1),定义一条路径的架势为这条路径上所有边边权的(gcd),要求对于任意(iin[1,n]),求长度为(i)的路径的最大权值
(nleq 4 imes 10^5,wleq 10^6)
不难发现长度越小的路径,其最大权值也就越大,因此答案单调不降
考虑一个(O(nw))的做法,即枚举(gcd),求当前(gcd)下的最长路径;每次只走边权为(i)的倍数的边,找树上直径即可
这样每次跑一边整棵树太傻了,于是我们把边权为(i)的倍数的边都拿出来,跑树的直径即可,复杂度是所有边的约数个数和
代码
#include<bits/stdc++.h>
#define re register
#define max(a,b) ((a)>(b)?(a):(b))
inline int read() {
char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
std::vector<int> v[1000001];
const int maxn=4e5+5;
struct E{int v,nxt;}e[maxn<<1];
int head[maxn],st[maxn],vis[maxn],top,num,cnt,n,T,tot;
int rx[maxn],ry[maxn],ans[maxn],f[maxn],tax[maxn],dep[maxn];
inline void add(int x,int y) {
e[++num].v=y;e[num].nxt=head[x];head[x]=num;
if(!vis[x]) st[++top]=x,vis[x]=1;
}
void dfs(int x,int fa,int o) {
if(o) f[x]=1,tax[++cnt]=x;
for(re int i=head[x];i;i=e[i].nxt) {
if(e[i].v==fa) continue;
dep[e[i].v]=dep[x]+1;
dfs(e[i].v,x,o);
}
}
inline void getdis(int x) {
cnt=0;dep[x]=0;dfs(x,0,1);int rt=tax[1];
for(re int i=2;i<=cnt;++i) if(dep[tax[i]]>dep[rt]) rt=tax[i];
dep[rt]=0;dfs(rt,0,0);
for(re int i=1;i<=cnt;i++) tot=max(tot,dep[tax[i]]);
}
int main() {
n=read();
for(re int w,i=1;i<n;i++) {
rx[i]=read(),ry[i]=read();
w=read(),T=max(T,w);
v[w].push_back(i);
}
for(re int i=1;i<=T;++i,tot=0,num=0) {
for(re int j=i;j<=T;j+=i)
for(re int k=0;k<v[j].size();k++)
add(rx[v[j][k]],ry[v[j][k]]),add(ry[v[j][k]],rx[v[j][k]]);
for(re int j=1;j<=top;++j) {
if(f[st[j]]) continue;
getdis(st[j]);
}
ans[tot]=i;
while(top) f[st[top]]=vis[st[top]]=head[st[top]]=0,top--;
}
for(re int i=n-1;i;--i) ans[i]=max(ans[i],ans[i+1]);
for(re int i=1;i<=n;i++) printf("%d
",ans[i]);
return 0;
}
( m T3)给定(n,L,s)和一个严格上升的数组({x_1,x_2,...,x_n}),需要求一个排列(p),满足(p_1=s),且(p_i<p_{i+1})恰有(L)项,最小化
写了个假贪心之后果真假了,之后发现全场都假了
看了看成爷的假贪心发现我们好像假的差不多,但是不知道为什么我(20 m pts)他(35 m pts)
正解非常巧妙
不妨考虑(s=1)的情况,如果我们要从(s)走到(n),如果没有(L)的限制,最优的方案就是直接从(1)走过去,但是有了(L)的限制,就要求我们必须走一些回头路
首先(1)到(n)之间的所有线段都至少被经过了一次,由于我们必须走(L)次回头路,所有至少有(L)个(x_i-x_{i-1})额外经过了两次,即从前面走回来再走回去,所以从(1)走到(n)中间需要走(L)次回头路的最小花费就是(x_n-x_1),加上前(L)小的(x_i-x_{i-1}),显然(L)越小这个花费也就越小
再来考虑终点不在(n)的情况,考虑枚举一个终点(e),那么在([1,s])和([e,n])这两段里要花费的(L)次数就是(s-1+n-e),同时里面的这些线段也会被经过两次
这样在两边的花费都尽量小,同时还能减小在([s,e])之间需要花费的(L)的次数;这个时候我们发现([s,e])的情况和([1,n])的情况是等价的,也就是也就是说我们需要维护([s,e])之间前(L-s+1-n+e)小的(x_i-x_{i-1})
随着(e)的增大,我们发现影响的只有(x_{e}-x_{e-1})这一条线段,于是我们用一个堆就能维护了
代码就不写了
从这次考试还是能看出自己非常垃圾了,首先就是在没有大样例的情况下挂题的概率趋近于1,还懒得去写暴力和搭拍,于是往往搞出来的题也挂的非常惨
时间分配上也非常弟弟,( m T3)的假贪心写了一个多小时,( m T1)推到最后才写出来,留给( m T2)的时间就只够写个暴力了
之后还是智商太低了,( m T1)这样的结论题,成爷(10 m min)就切了,我这种睿智选手搞到最后半个小时才搞出来,之后还写挂了;( m T3)的贪心还看不出来假了,于是兴致盎然的写了好久
看来下次得对着成爷的屏幕白嫖了