2017 清北济南考前刷题Day 3 afternoon
期望得分:100+40+100=240
实际得分:100+40+100=240
将每个联通块的贡献乘起来就是答案
如果一个联通块的边数>点数 ,那么无解
如果边数=点数,那么贡献是 2
如果边数=点数-1,那么贡献是点数
#include<queue> #include<cstdio> #include<iostream> using namespace std; const int mod=1e9+7; #define N 100001 int front[N],to[N<<1],nxt[N<<1],tot; bool vis[N]; int d[N]; queue<int>q; int ans=1; void read(int &x) { x=0; char c=getchar(); while(!isdigit(c)) c=getchar(); while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); } } void add(int u,int v) { to[++tot]=v; nxt[tot]=front[u]; front[u]=tot; to[++tot]=u; nxt[tot]=front[v]; front[v]=tot; d[v]++; } bool bfs(int s) { while(!q.empty()) q.pop(); int p=0,e=0; q.push(s); vis[s]=true; int now; while(!q.empty()) { now=q.front(); q.pop(); p++; e+=d[now]; for(int i=front[now];i;i=nxt[i]) if(!vis[to[i]]) vis[to[i]]=true,q.push(to[i]); } if(e>p) return false; if(e==p) ans=ans*2%mod; else ans=1ll*ans*p%mod; return true; } int main() { freopen("girl.in","r",stdin); freopen("girl.out","w",stdout); int n,m; read(n); read(m); int u,v; for(int i=1;i<=m;i++) { read(u); read(v); add(u,v); } for(int i=1;i<=n;i++) if(!vis[i]) if(!bfs(i)) { cout<<0; return 0; } cout<<ans; return 0; }
显然的结论:
若一个数的K进制和-k进制相同
那么他的k进制/-k进制的偶数位一定是0
然后乱搞就好了
也可以数位DP
#include<iostream> #include<cstdio> #include<cmath> using namespace std; typedef long long LL; LL bit[67]; int a[67]; int main() { freopen("endless.in","r",stdin); freopen("endless.out","w",stdout); long long n;int k;LL ans=0; scanf("%I64d%d",&n,&k); int len=0; while(n) a[++len]=n%k,n/=k; if(!(len&1)) { ans=pow(1LL*k,len/2); cout<<ans; } else { for(int i=len;i>=1;i--) if(a[i]) { if(!(i&1)) { ans+=pow(1LL*k,i/2); break; } ans+=1LL*a[i]*pow(1LL*k,i/2); if(i==1) ans++; } cout<<ans; } }
40暴力
#include<cmath> #include<cstdio> #include<cstring> #include<iostream> using namespace std; int fbit[1001]; int a[1001],b[1001]; int pre[1001]; int main() { freopen("endless.in","r",stdin); freopen("endless.out","w",stdout); int n,k; scanf("%d%d",&n,&k); fbit[0]=1; int la,lb; int x,ans=min(k-1,n)+1; for(int i=k;i<=n;i++) { la=0; x=i; while(x) a[la++]=x%k,x/=k; la--; x=i; int c=-1,now=0; pre[0]=k-1; while(1) { fbit[++now]=fbit[now-1]*c*k; if(!(now&1)) { pre[now]=pre[now-2]+(k-1)*fbit[now]; if(pre[now]>=x) break; } else pre[now]=pre[now-1]; } lb=now; memset(b,0,sizeof(b)); while(now) { if(!(now&1)) while(x>pre[now-2]) b[now]++,x-=fbit[now]; else { while(x<0 && abs(x)>pre[now-1]) b[now]++,x-=fbit[now]; while(x>pre[now-1]) b[now]++,x+=fbit[now]; } now--; } b[0]=x; if(la!=lb) continue; bool ok=true; for(int i=0;i<=la && ok ;i++) if(a[i]!=b[i]) ok=false; if(!ok) continue; // printf("%d\n",i); ans++; } cout<<ans; }
考场思路:
每次旅行一定是找当前贡献最大的叶子节点
用线段树维护所有的叶子节点的贡献
修改:
每个点只会修改一次
所以用并查集记录这个点到根节点路径上第一个没有被修改的点
修改沿着并查集的father找上去
对于每个要改的点,预处理出它会影响到的叶子节点,
按dfs到的叶子节点的顺序在线段树中加点,那每个点影响到的叶子节点就是一段连续的区间
dfs记下来,线段树区间修改即可
其实可以不用并查集
往上改到已经改过的点,直接break
#include<cstdio> #include<iostream> #include<algorithm> using namespace std; #define N 100001 typedef long long LL; int n,m; int val[N]; int front[N],nxt[N<<1],to[N<<1],tot; int F[N],fa[N],id[N]; int cnt,L[N],R[N]; LL w[N]; LL mx[N<<2],pos[N<<2],tag[N<<2]; void read(int &x) { x=0; char c=getchar(); while(!isdigit(c)) c=getchar(); while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); } } void add(int u,int v) { to[++tot]=v; nxt[tot]=front[u]; front[u]=tot; to[++tot]=u; nxt[tot]=front[v]; front[v]=tot; } void init() { read(n); read(m); for(int i=1;i<=n;i++) read(val[i]); int u,v; for(int i=1;i<n;i++) { read(u); read(v); add(u,v); } } void dfs(int x,int f,LL sum) { L[x]=cnt+1; F[x]=x; bool leaf=true; for(int i=front[x];i;i=nxt[i]) if(to[i]!=f) leaf=false,fa[to[i]]=x,dfs(to[i],x,sum+val[to[i]]); if(leaf) w[++cnt]=sum,id[cnt]=x; R[x]=cnt; } void build(int k,int l,int r) { if(l==r) { mx[k]=w[l]; pos[k]=l; return; } int mid=l+r>>1; build(k<<1,l,mid); build(k<<1|1,mid+1,r); mx[k]=max(mx[k<<1],mx[k<<1|1]); pos[k]= mx[k]==mx[k<<1] ? pos[k<<1] : pos[k<<1|1]; } void down(int k) { mx[k<<1]-=tag[k]; mx[k<<1|1]-=tag[k]; tag[k<<1]+=tag[k]; tag[k<<1|1]+=tag[k]; tag[k]=0; } void change(int k,int l,int r,int opl,int opr,LL sum) { if(l>=opl && r<=opr) { mx[k]-=sum; tag[k]+=sum; return; } if(tag[k]) down(k); int mid=l+r>>1; if(opl<=mid) change(k<<1,l,mid,opl,opr,sum); if(opr>mid) change(k<<1|1,mid+1,r,opl,opr,sum); mx[k]=max(mx[k<<1],mx[k<<1|1]); pos[k]=mx[k]==mx[k<<1] ? pos[k<<1] : pos[k<<1|1]; } int find(int i) { return F[i]==i ? i : F[i]=find(F[i]); } void solve() { int p; LL ans=0; while(m--) { p=id[pos[1]]; // 第pos个叶子节点 ans+=mx[1]; while(p) { change(1,1,cnt,L[p],R[p],val[p]); F[p]=find(F[fa[p]]); p=F[p]; } } cout<<ans; } int main() { freopen("tour.in","r",stdin); freopen("tour.out","w",stdout); init(); dfs(1,0,val[1]); build(1,1,cnt); solve(); }