noip模拟测试19
T1:Count
首先容易发现块的大小一定是n的约数,枚举约数$O(\sqrt n)$
考虑怎么判定
设块大小为k
发现只要最下方的子树$(a)$大小是$k$,包含a的子树$(b)$大小是$2k$,包含b的子树$(c)$大小是$3k$……
即:只需要有$n/k$个子树的大小是$k$的倍数即可
所以就可以$O(n)$的判定了,然后就无脑提交$O(n \sqrt n )$过百万???
#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<iostream> #include<queue> #include<cstdlib> #include<vector> using namespace std; const int MAXN=1000005; int n,ans,siz[MAXN]; struct node { int to,nxt; }mp[MAXN*2]; int h[MAXN],tot; void dfs(int u,int fa) { siz[u]=1; for(int i=h[u];i;i=mp[i].nxt) { int v=mp[i].to; if(v==fa) continue; dfs(v,u); siz[u]+=siz[v]; } } inline int R() { int a=0;char c=getchar(); while(c>'9'||c<'0')c=getchar(); while(c>='0'&&c<='9')a=a*10+c-'0',c=getchar(); return a; } int main() { n=R(); for(int i=1,aa,bb;i<n;++i) { aa=R();bb=R(); mp[++tot].nxt=h[aa]; mp[tot].to=bb;h[aa]=tot; mp[++tot].nxt=h[bb]; mp[tot].to=aa;h[bb]=tot; } dfs(1,0); for(int i=2;i<n;++i) if(n%i==0) { int cnt=0; for(int j=1;j<=n;j++) if(siz[j]%i==0) ++cnt; if(n/i==cnt) ++ans; } printf("%d\n",ans+2); return 0; }
T2:Dinner
没想到倍增,想到了二分可以跳到的点,但m的范围没有给,若m与n同级复杂度就爆了,所以没写
结果发现m实际上很小,好多人都用二分水过了……
首先断环成链,二分答案
最暴力的判定是枚举端点,再一步一步贪心的跳,复杂度$O(n^2logn)$
考虑优化跳的复杂度
对于每次check,可以$O(nlogn)$的预处理跳$2^n$步可以跳到哪里
然后把m二进制拆开跳,复杂度$O((nlogn+logm)*logn)$,若m与n同级$O(nlog^2n)$
1 #include<cstdio> 2 #include<cstring> 3 #include<cmath> 4 #include<algorithm> 5 #include<iostream> 6 #include<queue> 7 #include<cstdlib> 8 #include<vector> 9 using namespace std; 10 const int MAXN=50005; 11 int n,m,ans,suma,mx,t[MAXN*2],nxt[MAXN*2][20]; 12 inline int R() { 13 int a=0;char c=getchar(); 14 while(c>'9'||c<'0')c=getchar(); 15 while(c>='0'&&c<='9')a=a*10+c-'0',c=getchar(); 16 return a; 17 } 18 bool check(int lim) { 19 for(int i=1;i<=n;i++) { 20 int pos=i,tm=m; 21 for(int j=18;j>=0;j--) { 22 if((1<<j)>tm) continue; 23 pos=nxt[pos][j]; 24 tm-=(1<<j); 25 } 26 if(pos-i>=n) return 1; 27 } 28 return 0; 29 } 30 void first(int lim) { 31 int l=1,sum=0; 32 for(int i=1;i<=2*n;i++) 33 if(sum+t[i]<=lim) sum+=t[i]; 34 else { 35 while(l<i&&sum+t[i]>lim) { 36 nxt[l][0]=i; 37 sum-=t[l]; 38 ++l; 39 } 40 sum+=t[i]; 41 } 42 while(l<=2*n) nxt[l][0]=2*n+1,++l; 43 for(int i=0;i<=18;i++) nxt[2*n+1][i]=2*n+1; 44 for(int i=1;i<=18;i++) 45 for(int j=1;j+(1<<(i-1))<=2*n;j++) 46 nxt[j][i]=nxt[nxt[j][i-1]][i-1]; 47 } 48 int main() { 49 scanf("%d%d",&n,&m); 50 for(int i=1;i<=n;++i) scanf("%d",&t[i]); 51 for(int i=1;i<=n;++i) 52 t[i+n]=t[i],suma+=t[i],mx=max(mx,t[i]); 53 int l=mx,r=suma; 54 while(l<=r) { 55 int mid=(l+r)>>1; 56 first(mid); 57 if(check(mid)) 58 ans=mid,r=mid-1; 59 else l=mid+1; 60 } 61 printf("%d\n",ans); 62 return 0; 63 }
T3:Chess
首先第一问很简单,可以直接双端队列bfs或建图跑最短路
然后看第二问,发现只有走的空地不同才算不同的方案
发现n的范围很小,于是对于dfs每一个敌军的连通块,看它连接着哪些空地,在这些空地间两两连边
再将直接可以到达的两空地间连边
特别的:可以将敌人的帅看做一个空地进行连边
跑最短路计数就行了
注意:相同的边只能连一次,所以可以用邻接矩阵来存边
(有点毒瘤……)
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<cstdlib> 5 #include<cmath> 6 #include<iostream> 7 #include<queue> 8 #include<vector> 9 #define ll long long 10 using namespace std; 11 const int MAXN=70,INF=0x3f3f3f3f; 12 int n,m,bx,by,ex,ey,eid,s[MAXN][MAXN]; 13 int dx[8]={-1,-2,-2,-1,1,2,2,1},dy[8]={-2,-1,1,2,2,1,-1,-2}; 14 struct node { 15 int to,nxt,dis; 16 }mp[MAXN*MAXN*8]; 17 int h[MAXN*MAXN],tot; 18 int id(int x,int y) { 19 return (x-1)*m+y; 20 } 21 void add(int x,int y,int z) { 22 mp[++tot].dis=z; 23 mp[tot].to=y; 24 mp[tot].nxt=h[x]; 25 h[x]=tot; 26 } 27 void build() { 28 for(int i=1;i<=n;i++) { 29 for(int j=1;j<=m;j++) { 30 if(s[i][j]==2) continue; 31 if(s[i][j]==4) {ex=i,ey=j;continue;} 32 if(s[i][j]==3) bx=i,by=j; 33 for(int k=0;k<8;k++) { 34 int nx=i+dx[k],ny=j+dy[k]; 35 if(nx<1||ny<1||nx>n||ny>m) continue; 36 if(s[nx][ny]==2) continue; 37 add(id(i,j),id(nx,ny),s[nx][ny]!=1&&s[nx][ny]!=4); 38 } 39 } 40 } 41 } 42 int ver[MAXN*MAXN][MAXN*MAXN],dis[MAXN*MAXN]; 43 vector<int> tver; 44 bool vis[MAXN*MAXN],flag; 45 void dfs(int u) { 46 for(int i=h[u];i;i=mp[i].nxt) { 47 int v=mp[i].to; 48 if(vis[v]) continue; 49 vis[v]=1; 50 if(v==eid) flag=1; 51 if(!mp[i].dis) dfs(v); 52 else if(mp[i].dis==1) tver.push_back(v); 53 } 54 } 55 void new_build() { 56 eid=id(ex,ey); 57 memset(vis,0,sizeof(vis)); 58 for(int i=1;i<=n;i++) { 59 for(int j=1;j<=m;j++) if(s[i][j]==1){ 60 tver.clear();flag=0; 61 vis[id(i,j)]=1;dfs(id(i,j)); 62 for(int k=0;k<tver.size();++k) 63 for(int l=0;l<tver.size();++l) 64 ver[tver[k]][tver[l]]=ver[tver[l]][tver[k]]=1; 65 if(flag) for(int k=0;k<tver.size();++k) 66 ver[tver[k]][eid]=1; 67 memset(vis,0,sizeof(vis)); 68 } 69 } 70 for(int i=1;i<=n;i++) 71 for(int j=1;j<=m;j++) if(s[i][j]==0||s[i][j]==3) 72 for(int k=h[id(i,j)];k;k=mp[k].nxt) 73 if(mp[k].dis||mp[k].to==eid) 74 ver[id(i,j)][mp[k].to]=ver[mp[k].to][id(i,j)]=1; 75 } 76 ll cnt[MAXN*MAXN]; 77 void get_cnt() { 78 memset(dis,0x3f,sizeof(dis)); 79 dis[id(bx,by)]=0;cnt[id(bx,by)]=1; 80 priority_queue<pair<int,int> > q; 81 q.push(make_pair(0,id(bx,by))); 82 while(!q.empty()) { 83 int u=q.top().second; q.pop(); 84 if(vis[u]) continue; 85 vis[u]=1; 86 for(int i=1;i<=n*m;i++) if(i!=u){ 87 int v=i,ds=ver[u][i]; 88 if(!ds) continue; 89 if(dis[v]>dis[u]+ds) { 90 dis[v]=dis[u]+ds; 91 cnt[v]=cnt[u]; 92 q.push(make_pair(-dis[v],v)); 93 } else if(dis[v]==dis[u]+ds) cnt[v]+=cnt[u]; 94 } 95 } 96 printf("%lld\n",cnt[eid]); 97 } 98 void dijkstra() { 99 priority_queue<pair<int,int> > q; 100 q.push(make_pair(0,id(bx,by))); 101 memset(dis,0x3f,sizeof(dis)); 102 dis[id(bx,by)]=0; 103 while(!q.empty()) { 104 int u=q.top().second; q.pop(); 105 if(vis[u]) continue; 106 vis[u]=1; 107 for(int i=h[u];i;i=mp[i].nxt) { 108 int v=mp[i].to,ds=mp[i].dis; 109 if(dis[v]>dis[u]+ds) { 110 dis[v]=dis[u]+ds; 111 q.push(make_pair(-dis[v],v)); 112 } 113 } 114 } 115 if(dis[id(ex,ey)]==INF) { 116 printf("-1\n");exit(0); 117 } else printf("%d\n",dis[id(ex,ey)]); 118 } 119 int main() { 120 scanf("%d%d",&n,&m); 121 for(int i=1;i<=n;i++) 122 for(int j=1;j<=m;j++) 123 scanf("%d",&s[i][j]); 124 build(); 125 dijkstra(); 126 new_build(); 127 get_cnt(); 128 return 0; 129 }