大体状况
240/300
T1 simple
题目大意
求 [1,q] 内满足有非负整数解 xn+ym=c 的数的个数。
分析
P60
这个不定方程虽然是exgcd的形式,
然而完全可以写DP,然后直接计算。
实际上就是个暴力。
P100
因为不会写,
又看到
n≤105
,
就考虑在
modn
下讨论,
然后调了一会凑出了答案,对拍无误。
代码
瞎写。
#include<bits/stdc++.h>
using namespace std;
#define Komachi is retarded
#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;i++)
#define DREP(i,a,b) for(int i=(a),i##_end_=(b);i>i##_end_;i--)
#define chkmin(a,b) a=min(a,b)
#define chkmax(a,b) a=max(a,b)
#define LL long long
void Rd(int &res){
char c;res=0;
while((c=getchar())<48);
do res=(res<<3)+(res<<1)+(c^48);
while((c=getchar())>47);
}
void Rd(LL &res){
char c;res=0;
while((c=getchar())<48);
do res=(res<<3)+(res<<1)+(c^48);
while((c=getchar())>47);
}
int T,n[14],m[14];
LL q[14],mx;
struct P60{
static const int M=100004;
int DP[M];
void Solve(){
REP(t,0,T){
REP(i,0,M)if(i%n[t])DP[i]=0;
else DP[i]=1;
REP(i,0,M-m[t]-1)if(DP[i])
DP[i+m[t]]=1;
REP(i,0,M)DP[i]=!DP[i];
REP(i,1,M)DP[i]+=DP[i-1];
printf("%d\n",DP[q[t]]);
}
}
}P60;
struct P100{
static const int M=100004;
LL DP[M];
int Q[M];
void Solve(){
REP(t,0,T){
memset(DP,63,sizeof(DP));
int A=DP[0]=0;
LL val=0;
while(1){
(A+=m[t])%=n[t];
val+=m[t];
if(val>q[t])break;
if(val<DP[A])DP[A]=val;
else break;
}
LL Ans=q[t];
REP(i,0,M)if(DP[i]!=0x3f3f3f3f3f3f3f3f){
Ans-=(q[t]-DP[i])/n[t]+1;
}
Ans++;
printf("%lld\n",Ans);
}
}
}P100;
int main(){
freopen("simple.in","r",stdin);
freopen("simple.out","w",stdout);
Rd(T);
REP(t,0,T)Rd(n[t]),Rd(m[t]),Rd(q[t]),chkmax(mx,q[t]);
if(mx<=100000)P60.Solve();
else P100.Solve();
return 0;
}
T2 walk
题目大意
求树上每一种长度的路径所得到的边权最大Gcd。
P30
暴力枚举一个起点。
暴力搜索同时计算Gcd。
暴力更新答案。
P100
然后发现答案应该是单调递减的。
边权的范围较小,所以可以枚举答案。
然后用边权为答案的倍数的这些边建森林,
求每棵树的直径,取最大值用于更新即可。
代码
比赛时没有枚举答案倍数,弄了一个枚举边权因子。
为此写了线性筛素数与其副产品线性拆质因子。
以及DFS枚举因子和时间戳类链表。
速度十分慢,还好是卡过去了。
#include<bits/stdc++.h>
using namespace std;
#define Komachi is retarded
#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;i++)
#define DREP(i,a,b) for(int i=(a),i##_end_=(b);i>i##_end_;i--)
#define LL long long
#define chkmin(a,b) a=min(a,b)
#define chkmax(a,b) a=max(a,b)
#define M 400004
#define pb push_back
#define SZ(a) ((int)(a).size())
void Rd(int &res){
char c;res=0;
while((c=getchar())<48);
do res=(res<<3)+(res<<1)+(c^48);
while((c=getchar())>47);
}
#define LREP(i,A) for(int i=Head[A];i;i=Next[i])
int n;
struct P30{
int Next[M<<1],V[M<<1],W[M<<1],Head[M],tot;
void Add_Edge(int u,int v,int w){
Next[++tot]=Head[u],V[Head[u]=tot]=v,W[tot]=w;
}
int Gcd(int a,int b){
return b?Gcd(b,a%b):a;
}
int Ans[1004];
void DFS(int A,int f,int d,int num){
int B;
if(d)chkmax(Ans[d],num);
LREP(i,A)if((B=V[i])!=f)
DFS(B,A,d+1,Gcd(W[i],num));
}
void Solve(){
REP(i,1,n){
int u,v,w;
Rd(u),Rd(v),Rd(w);
Add_Edge(u,v,w);
Add_Edge(v,u,w);
}
memset(Ans,0,sizeof(Ans));
REP(i,1,n+1)DFS(i,0,0,0);
REP(i,1,n+1)printf("%d\n",Ans[i]);
}
}P30;
struct P100{
static const int Mx=1000004;
int Pri[Mx>>1],pt;
int B[Mx];
int U[M],V[M],Tmp[20];
bool Vis[20];
int Ans[M],space;
vector<int>Use[Mx];
void Updata(int x,int cnt,int num,int id){
if(x==cnt)return;
if(x && !Vis[x-1] && Tmp[x]==Tmp[x-1]){
Updata(x+1,cnt,num,id);
return;
}
Updata(x+1,cnt,num,id);
Vis[x]=1;
num*=Tmp[x];
Use[num].pb(id);space++;
Updata(x+1,cnt,num,id);
Vis[x]=0;
}
void Init(){
REP(i,2,Mx){
if(!B[i])Pri[pt++]=B[i]=i;
REP(j,0,pt){
LL pos=1ll*i*Pri[j];
if(pos>=Mx)break;
B[pos]=Pri[j];
if(!(i%Pri[j]))break;
}
}
}
int Next[M<<1],Go[M<<1],Tim[M],Head[M],tot,Time;
void Add_Edge(int u,int v){
if(Tim[u]!=Time)Tim[u]=Time,Head[u]=0;
Next[++tot]=Head[u],Go[Head[u]=tot]=v;
}
void TreeClear(){
Time++;
tot=0;
}
bool Mark[M];
int Dis[M];
int DFS(int A,int f,int &To){
Mark[A]=1;
Dis[A]=Dis[f]+1;
if(Dis[A]>Dis[To])To=A;
int B;
LREP(i,A)if((B=Go[i])!=f)
DFS(B,A,To);
}
void Check(int PAns){
if(!SZ(Use[PAns]))return;
TreeClear();
int sz=0;
REP(i,0,SZ(Use[PAns])){
int j=Use[PAns][i];
Mark[U[j]]=Mark[V[j]]=0;
Add_Edge(U[j],V[j]);
Add_Edge(V[j],U[j]);
}
int Res=1;
REP(i,0,SZ(Use[PAns])){
int j=Use[PAns][i];
if(!Mark[U[j]]){
int LT,RT;LT=RT=0;
Dis[0]=-1;
DFS(U[j],0,LT);
DFS(LT,0,RT);
chkmax(Res,Dis[RT]);
}
}
// cerr<<Res<<","<<PAns<<endl;
chkmax(Ans[Res],PAns);
}
void Solve(){
Init();
memset(Ans,0,sizeof(Ans));
REP(i,1,n){
int cnt=0,w;
Rd(U[i]),Rd(V[i]),Rd(w);
chkmax(Ans[1],w);
while(B[w])Tmp[cnt++]=B[w],w/=B[w];
Updata(0,cnt,1,i);
Add_Edge(U[i],V[i]);
Add_Edge(V[i],U[i]);
}
int LT,RT;LT=RT=0;
Dis[0]=-1;
DFS(1,0,LT);
DFS(LT,0,RT);
chkmax(Ans[Dis[RT]],1);
REP(i,2,Ans[1]+1)
Check(i);
DREP(i,n,1)
chkmax(Ans[i],Ans[i+1]);
REP(i,1,n+1)
printf("%d\n",Ans[i]);
}
}P100;
int main(){
freopen("walk.in","r",stdin);
freopen("walk.out","w",stdout);
Rd(n);
if(n<=1000)P30.Solve();
else P100.Solve();
// cerr<<(sizeof(P30)+sizeof(P100)+P100.space*4.0)/1024/1024<<endl;
return 0;
}
T3 travel
题目大意
在数轴上瞎跳,求从s开始刚好向左跳L次并刚好经过每个点一次的最小距离以及这个跳跃方案。
P10
O(n!) 暴力搜索所有方案。
P???
比赛时间还剩30分钟
思考一下,发现很可能不走回头路,
于是就分为两种情况直接贪心了。
然而这个贪心是错的。
然后水了40分。
P100
然后确实是贪心,但是不是那么蠢。
还是会走回头路的,此时可以用掉一个L。
代价就是这一条线段要走3遍。
枚举结束节点e,使s始终在e左侧。
然后可以得到一种方案,
在s左侧都走两遍,在e右侧也走两遍,用掉n-e+s-1个L,
此时在中间取L个最小线段走三次即可。
然后反向搜索,
由于取的线段一直在变少,可以直接用一个队列维护。
找到答案后随便求出一个可行的方案即可。
代码
感觉几乎是抄题解。
#include<bits/stdc++.h>
using namespace std;
#define Komachi is retarded
#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;i++)
#define DREP(i,a,b) for(int i=(a),i##_end_=(b);i>i##_end_;i--)
#define chkmin(a,b) a=min(a,b)
#define chkmax(a,b) a=max(a,b)
#define LL long long
#define INF 0x3f3f3f3f3f3f3f3f
void Rd(int &res){
char c;res=0;
while((c=getchar())<48);
do res=(res<<3)+(res<<1)+(c^48);
while((c=getchar())>47);
}
#define M 200004
int n,l,s,X[M],Y[M];
int Ans1[M],Ans2[M];
LL ans1,ans2;
struct Node{
int v,x;
bool operator <(const Node &P)const{
return v<P.v;
}
}D[M];
int Pos[M];
bool Mark[M];
void Solve(int l,int s,int *Ans,int *X,LL &ans){
int len=0,tot=0;
if(l<=s-1){
ans=0LL+X[n]-X[1]+X[s]-X[1];
DREP(i,l,0)Ans[len++]=i;
REP(i,l+1,n+1)if(i!=s)Ans[len++]=i;
return;
}
l-=s-1;
if(l==n-s-1){
ans=0LL+X[n]-X[1]+X[s]-X[1]+X[n]-X[s+1];
REP(i,1,s)Ans[len++]=i;
DREP(i,n,s)Ans[len++]=i;
return;
}
LL Sum=0,Mn,Val;
REP(i,s+2,n) D[++tot]=(Node){X[i]-X[i-1],i};
sort(D+1,D+tot+1);
REP(i,1,tot+1)Pos[D[i].x]=i;
REP(i,1,l+1)Sum+=D[i].v;
Mn=Sum<<1;
int mn=l,p=l,e=n;
DREP(i,n-1,n-l-1){
if(Pos[i]<=p)Sum-=D[Pos[i]].v;
else Sum-=D[p--].v;
while(p && D[p].x>=i)p--;
if((Val=((Sum<<1)+X[n]-X[i]))<Mn)
Mn=Val,e=i,mn=p;
}
DREP(i,s-1,0)Ans[len++]=i;
REP(i,s+2,e)Mark[i]=Pos[i]<=mn;
for(int i=s+1;i<e;i++){
int tmp=i+1;
if(!Mark[tmp])Ans[len++]=i;
else {
while(Mark[tmp])tmp++;
DREP(j,tmp-1,i-1)Ans[len++]=j;
i=tmp-1;
}
}
DREP(i,n,e-1)Ans[len++]=i;
ans=0LL+X[n]-X[1]+X[s]-X[1]+Mn;
}
int main(){
Rd(n),Rd(l),Rd(s);
REP(i,1,n+1)Rd(X[i]),Y[n-i+1]=-X[i];
if((l==0 && s!=1) || (l==n-1 && s!=n) || l>n-1){
puts("-1");
return 0;
}
Solve(l,s,Ans1,X,ans1);
Solve(n-l-1,n-s+1,Ans2,Y,ans2);
if(ans1<=ans2){
printf("%lld\n",ans1);
REP(i,0,n-1)printf("%d%c",Ans1[i]," \n"[i==n-2]);
}
else{
printf("%lld\n",ans2);
REP(i,0,n-1)printf("%d%c",n-Ans2[i]+1," \n"[i==n-2]);
}
return 0;
}
总结
其实好像是Rank1来着,
?运气比较好,大概吧。
注意一下实现了,T2写得太麻烦了,
数据卡一下可能还会因为常数大的关系死掉。