noip2013
D1:
T1:快速幂
#include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #include<cstdlib> #include<cmath> #define LL long long using namespace std; LL n,m,k,x; inline LL quickpow(LL a,LL b){//a^b %n LL ans=1ll; while(b){ if(b&1)ans=ans*a%n; a=a*a%n; b>>=1; } return ans; } int main(){ //freopen("circle.in","r",stdin); //freopen("circle.out","w",stdout); scanf("%lld%lld%lld%lld",&n,&m,&k,&x); printf("%lld",(x%n+m*quickpow(10ll,k)%n)%n); return 0; }
T2:
#include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #include<cstdlib> #include<cmath> #include<map> #define LL long long using namespace std; const int maxn=1e5+5; const int P=99999997; int n; int a[maxn],b[maxn],to[maxn],ans; map<int ,int >pa; map<int ,int >pb; int sum[maxn]; inline int lowbit(int x){return x&(-x);} inline int query(int p){ int ans=0; while(p){ ans+=sum[p]; p-=lowbit(p); } return ans; } inline void modify(int p){ while(p<=n){ sum[p]++; p+=lowbit(p); } } int main(){ //freopen("match.in","r",stdin); //freopen("match.out","w",stdout); scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); pa[a[i]]=i; } for(int i=1;i<=n;i++){ scanf("%d",&b[i]); pb[b[i]]=i; } sort(a+1,a+n+1); sort(b+1,b+n+1); /*
这里用到了排序不等式,(a-b)^2==a^2+b^2-2ab, 找到一个排列 s.t. -2ab max 自然想到排序不等式
正序和 <= 乱序和 <= 逆序和
*/ for(int i=1;i<=n;i++){ to[ pa[a[i]] ]=pb[b[i]]; } memset(sum,0,sizeof(sum)); ans=0; for(int i=n;i;i--){//由于是相邻的两个火柴交换,直接求逆序对 ans=(ans+query(to[i]-1))%P; modify(to[i]); } printf("%d\n",ans); return 0; }
T3:
这题挺不好想,但是从部分分出发
30` && 60` 有向图两点间最小边最大,二分答案,O(q *log( m)* m )
但是其中填边的步骤可以省略,O(q*m)--------->kruscal 第一个使得两点联通的 边就是答案
100` 我们发现 每个询问都会填边太麻烦,然后我们想,推出两个性质
1:
如果该边是答案,那么他一定在该图的最大生成树中(反证法)
2:
如果该边是答案,那么两点联通 当且仅当 该边被添加(反证法)
于是就得到了满分算法,最大生成树+LCA最小边
#include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #include<cstdlib> #include<cmath> #define LL long long using namespace std; const int maxn=1e4+5; const int maxm=5e4+5; struct Edge{ int u; int v; int w; const bool operator<(const Edge &x)const{return w<x.w;} inline void read(){ scanf("%d%d%d",&u,&v,&w); } }e[maxm]; int n,m,q; int father[maxn]; int find(int x){return father[x]==x ? x : father[x]=find(father[x]);}//dsu int fst[maxn],nxt[maxm<<1],to[maxm<<1],w[maxm<<1],edge_count; inline void add(int x,int y,int z){ edge_count++; to[edge_count]=y; w[edge_count]=z; nxt[edge_count]=fst[x]; fst[x]=edge_count; } inline void kruscal(){ for(int i=1;i<=n;i++)father[i]=i;//dsu_init sort(e+1,e+m+1); for(int i=m;i;i--){//鏈€澶х敓鎴愭爲 int fu=find(e[i].u);int fv=find(e[i].v); if(fu==fv)continue; father[fu]=fv; add(e[i].u,e[i].v,e[i].w); add(e[i].v,e[i].u,e[i].w); } } const int INF=0x3f3f3f3f; int f[maxn][50],g[maxn][50],deep[maxn],tre[maxn],temp; void dfs(int u,int fa){ tre[u]=temp; deep[u]=deep[fa]+1; for(int i=1;i<30;i++){ f[u][i]=f[ f[u][i-1] ][i-1]; g[u][i]=min(g[ f[u][i-1] ][i-1],g[u][i-1]); } for(int i=fst[u];i;i=nxt[i]){ int v=to[i]; if(v==fa)continue; f[v][0]=u; g[v][0]=w[i]; dfs(v,u); } } inline void LCA_init(){ memset(g,0x3f,sizeof(g)); for(int i=1;i<=n;i++)if(!deep[i]){ temp++; dfs(i,0); } } inline int LCA(int x,int y){ int ans=INF; if(deep[x]<deep[y])swap(x,y); for(int i=29;i>=0;i--){ if(deep[f[x][i]]>=deep[y]){ ans=min(ans,g[x][i]); x=f[x][i]; } } if(x==y)return ans; for(int i=29;i>=0;i--){ if(f[x][i]!=f[y][i]){ ans=min(ans,min(g[x][i],g[y][i])); x=f[x][i];y=f[y][i]; } } ans=min(ans,min(g[x][0],g[y][0])); return ans; } int main(){ //freopen("truck.in","r",stdin); //freopen("truck.out","w",stdout); scanf("%d%d",&n,&m); for(int i=1;i<=m;i++){ e[i].read(); } kruscal(); LCA_init(); scanf("%d",&q); for(int i=1,x,y;i<=q;i++){ scanf("%d%d",&x,&y); if(tre[x]!=tre[y])printf("-1\n"); else printf("%d\n",LCA(x,y)); } return 0; }
D2:
T1:
#include<cstdio> #include<cstring> #include<cmath> #include<iostream> #include<cstdlib> #include<algorithm> #define LL long long using namespace std; const int maxn=1e5+5; int n,h[maxn]; LL ans; int main(){ // freopen("block.in","r",stdin); // freopen("block.out","w",stdout); scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%d",&h[i]); ans=0ll; for(int i=1;i<=n+1;i++){ if(h[i]<h[i-1]){ ans+=1ll*(h[i-1]-h[i]); } } printf("%lld\n",ans); return 0; }
T2:
O(nlogn)???
虽然不是正解但是写的很开心,O(n^2)Dp+segment tree 优化
每次x要找比x大的 将要下降的 Dp值的最大值 +1
同理
#include<cstdio> #include<cstring> #include<cmath> #include<iostream> #include<cstdlib> #include<algorithm> using namespace std; const int maxn=1e5+5; const int maxh=1e6+5; /* main thought: segment tree,维护两个,存储一下 以num结尾的将要上升的所有Dp值 以num结尾的将要下降的所有Dp值 每次x 从第一棵树里面找所有比他 小 的值中最大的Dp值 插入第二棵数中 从第二棵树里面找所有比他 大 的值中最大的Dp值 插入第一棵树中 */ struct Node{ int l; int r; int len; Node(int L=0,int R=0,int Len=0):l(L),r(R),len(Len){} inline void update(const Node&a,const Node&b){ len=max(a.len,b.len); } inline void modify(int x){ len=max(len,x); } }L[maxh<<2][2]; //0->将要上升,1->将要下降 void build(int k,int l,int r,bool b){ L[k][b]=Node(l,r,0); if(l==r)return; else{ int mid=(l+r)>>1; build(k<<1,l,mid,b); build(k<<1|1,mid+1,r,b); return; } } void modify(int k,int p,int x,bool b){ if(L[k][b].l==L[k][b].r) return L[k][b].modify(x); else{ int mid=(L[k][b].l+L[k][b].r)>>1; if(p<=mid)modify(k<<1,p,x,b); else modify(k<<1|1,p,x,b); return L[k][b].update(L[k<<1][b],L[k<<1|1][b]); } } int query(int k,int l,int r,bool b){//max query if(l<=L[k][b].l&&L[k][b].r<=r)return L[k][b].len; else{ int mid=(L[k][b].l+L[k][b].r)>>1; int ans=0; if(l<=mid)ans=max(ans,query(k<<1,l,r,b)); if(mid<r)ans=max(ans,query(k<<1|1,l,r,b)); return ans; } } int n,h[maxn]; //0->将要上升,1->将要下降 int main(){ //freopen("flower.in","r",stdin); //freopen("flower.out","w",stdout); scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%d",&h[i]); build(1,0,1000000,1); build(1,0,1000000,0); for(int i=1;i<=n;i++){ if(h[i]<1000000)modify(1,h[i],query(1,h[i]+1,1000000,1)+1,0);//将要下降的max +1 加到 将要上升 中 if(h[i]>0)modify(1,h[i],query(1,0,h[i]-1,0)+1,1);//将要上升的max +1 加到 将要下降 中 }
printf("%d\n",max( query(1,0,1000000,1) , query(1,0,1000000,0) ) ); return 0; }
注意:!!!线段树进行修改查询的时候,要注意边界!!!
神奇做法:O(n)!!!
据说是找到拐点然后+2
std:
#include<iostream> #include<algorithm> #include<cmath> #include<cstring> #include<cstdio> using namespace std; const int maxn = 1000000 + 10 ; int vis,ans,n,h[maxn]; int main() { //freopen("flower.in","r",stdin); //freopen("flower.out","w",stdout); cin>>n; for(int i=1;i<=n;i++) scanf("%d",&h[i]); if(h[2]>=h[1]) vis=1; for(int i=2;i<=n;i++) { if(i==n && !vis) {ans++;break;} if(vis) if(h[i]>h[i+1]) {ans++;vis=0;continue;} if(!vis) if(h[i]<h[i+1]){ans++;vis=1;continue;} } cout<<ans+1<<endl; return 0; }
T3:
个人感觉写了双向广搜,理论上能将复杂度降低到 开一个根号
但是 忘记了 无解的情况都会跑满,所以比 单项广搜还慢一会
60`:
#include<cstdio> #include<cstring> #include<cmath> #include<iostream> #include<cstdlib> #include<algorithm> using namespace std; /* thought: 广搜判最短时间: 时间上: 空间上:每种状态只和空白块位置和目标快位置有关 共30^4种状态。。。 but 30^4*500=5e8 双向广搜!!! 也行,但是终止状态是4种,目标块在目标位置,空白块在4周 */ int n,m,Q; bool stable[50][50]; struct Node{ int x,y; Node(int X=0,int Y=0):x(X),y(Y){} const bool operator==(const Node&a)const{ return x==a.x&&y==a.y; } inline bool limit(){//判断是否合法 return 0<x && x<=n && 0<y && y<=m && stable[x][y]; } inline void read(){scanf("%d%d",&x,&y);} inline void write(){printf("%d %d\n",x,y);} }e,s,t; //0->正向,1->反向 struct Queue{//每种状态 Node S,W; Queue(Node SS=Node(0,0),Node WW=Node(0,0)):S(SS),W(WW){} inline void write(){printf("~~~~~~~~~\n");S.write();W.write();printf("~~~~~~\n");} }q[32*32*32*32][2]; int vis[32][32][32][32][2];//时间戳 判是否到达过 int step[32][32][32][32][2];//每种状态的步骤数 int front[2],rear[2]; const int dx[4]={0,1,0,-1}; const int dy[4]={1,0,-1,0}; inline void bfs(int i){ //printf("------------%d\n",i); front[0]=front[1]=rear[0]=rear[1]=0; q[rear[0]++][0]=Queue(s,e); step[s.x][s.y][e.x][e.y][0]=0; vis[s.x][s.y][e.x][e.y][0]=i; //q[front[0]][0].write(); for(int j=0;j<4;j++){ Node a=Node(t.x+dx[j],t.y+dy[j]); //printf("\na::");a.write(); if(a.limit()){ q[rear[1]++][1]=Queue(t,a); step[t.x][t.y][a.x][a.y][1]=0; vis[t.x][t.y][a.x][a.y][1]=i; } } //printf("???%d %d ???",front[1],rear[1]); //for(int j=front[1];j<rear[1];j++)q[j][1].write(); //printf("begin to search:::"); while(front[0]<rear[0] && front[1]<rear[1]){ if(rear[0]-front[0]<rear[1]-front[1]){ Queue &bg=q[front[0]][0]; //printf("bg:");bg.write(); if(vis[ bg.S.x ][ bg.S.y ][ bg.W.x ][ bg.W.y ][1]==i){ printf("%d\n",step[ bg.S.x ][ bg.S.y ][ bg.W.x ][ bg.W.y ][0]+step[ bg.S.x ][ bg.S.y ][ bg.W.x ][ bg.W.y ][1]); return; } for(int j=0;j<4;j++){ Node a=Node(bg.W.x+dx[j],bg.W.y+dy[j]); if(a.limit()){ //a.write(); if(a==bg.S){ q[rear[0]][0]=Queue(bg.W,bg.S); } else{ q[rear[0]][0]=Queue(bg.S,a); } Queue &ed=q[rear[0]][0]; if(vis[ed.S.x][ed.S.y][ed.W.x][ed.W.y][0]!=i){ step[ed.S.x][ed.S.y][ed.W.x][ed.W.y][0]=step[ bg.S.x ][ bg.S.y ][ bg.W.x ][ bg.W.y ][0]+1; vis[ed.S.x][ed.S.y][ed.W.x][ed.W.y][0]=i; rear[0]++; } } } front[0]++; } else{ Queue &bg=q[front[1]][1]; //printf("bg:");bg.write(); if(vis[ bg.S.x ][ bg.S.y ][ bg.W.x ][ bg.W.y ][0]==i){ printf("%d\n",step[ bg.S.x ][ bg.S.y ][ bg.W.x ][ bg.W.y ][0]+step[ bg.S.x ][ bg.S.y ][ bg.W.x ][ bg.W.y ][1]); return; } for(int j=0;j<4;j++){ Node a=Node(bg.W.x+dx[j],bg.W.y+dy[j]); if(a.limit()){ //a.write(); if(a==bg.S){ q[rear[1]][1]=Queue(bg.W,bg.S); } else{ q[rear[1]][1]=Queue(bg.S,a); } Queue &ed=q[rear[1]][1]; if(vis[ed.S.x][ed.S.y][ed.W.x][ed.W.y][1]!=i){ step[ed.S.x][ed.S.y][ed.W.x][ed.W.y][1]=step[ bg.S.x ][ bg.S.y ][ bg.W.x ][ bg.W.y ][1]+1; vis[ed.S.x][ed.S.y][ed.W.x][ed.W.y][1]=i; rear[1]++; } } } front[1]++; } } printf("-1\n"); } int main(){ freopen("puzzle.in","r",stdin); freopen("puzzle.out","w",stdout); scanf("%d%d%d",&n,&m,&Q); //printf("%d %d %d\n",n,m,Q); for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){ int _x;scanf("%d",&_x);stable[i][j]=_x; } for(int i=1;i<=Q;i++){ e.read();//e.write(); s.read();//s.write(); t.read();//t.write(); bfs(i); //if(i<q)memset(vis,0,sizeof(vis)); } return 0; }
70`:
#include<iostream> #include<algorithm> #include<cmath> #include<cstring> #include<cstdio> #include<queue> using namespace std; const int maxm = 30 + 5; bool vis[maxm][maxm][maxm][maxm]; int map[maxm][maxm],n,m,q; int exe,eye,sxs,sys,txt,tyt; int dx[5]={0,0,0,1,-1},dy[5]={0,1,-1,0,0}; struct Node{int ex,ey,sx,sy,d;}; int bfs() { queue<Node> pq; pq.push((Node){exe,eye,sxs,sys,0}); vis[exe][eye][sxs][sys]=1; while(!pq.empty()) { int ex=pq.front().ex,ey=pq.front().ey,sx=pq.front().sx,sy=pq.front().sy,d=pq.front().d; pq.pop(); for(int i=1;i<=4;i++) { int nx=ex+dx[i],ny=ey+dy[i]; if(nx<1 || nx>n || ny<1 || ny>m || !map[nx][ny]) continue; if(nx==sx && ny==sy){ if(vis[nx][ny][ex][ey]) continue; if(ex==txt && ey==tyt) return d+1; else{ pq.push((Node){nx,ny,ex,ey,d+1});vis[nx][ny][ex][ey]=1; } } else { if(vis[nx][ny][sx][sy]) continue; pq.push((Node){nx,ny,sx,sy,d+1});vis[nx][ny][sx][sy]=1; } } } return -1; } int main() { freopen("puzzle.in","r",stdin); freopen("puzzle.out","w",stdout); cin>>n>>m>>q; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf("%d",&map[i][j]); for(int i=1;i<=q;i++) { memset(vis,0,sizeof(vis)); scanf("%d%d%d%d%d%d",&exe,&eye,&sxs,&sys,&txt,&tyt); cout<<(sxs==txt&&sys==tyt?0:bfs())<<endl; } return 0; }
std:懂了,就是写的非常曲折,总共改了10多次。。。
(ps:好题,值得一做)
1 #include<cstdio> 2 #include<cstring> 3 #include<cmath> 4 #include<iostream> 5 #include<cstdlib> 6 #include<algorithm> 7 using namespace std; 8 /* 9 thought: 10 暴力预处理+ 对没种状态建图+spfa 11 */ 12 const int INF=0x3f3f3f3f; 13 14 int n,m,Q; 15 bool stable[50][50]; 16 struct Node{ 17 int x,y; 18 Node(int X=0,int Y=0):x(X),y(Y){} 19 20 const bool operator==(const Node&a)const{ 21 return x==a.x&&y==a.y; 22 } 23 inline bool limit(){//判断是否合法 24 return 0<x && x<=n && 0<y && y<=m && stable[x][y]; 25 } 26 inline void read(){scanf("%d%d",&x,&y);} 27 inline void write(){printf("%d %d\n",x,y);} 28 }e,s,t; 29 const int dx[4]={0,1,0,-1}; 30 const int dy[4]={1,0,-1,0};//下,右,上,左 31 32 int fst[40000],nxt[1000000],to[1000000],w[1000000],edge_count=0; 33 inline void add(int x,int y,int z){ 34 edge_count++; 35 to[edge_count]=y; 36 w[edge_count]=z; 37 nxt[edge_count]=fst[x]; 38 fst[x]=edge_count; 39 } 40 inline int get_hash(int x,int y,int k){ 41 return y*1000+x*10+k; 42 }//状态转数字 43 inline Node get_Node(int a){ 44 int xx,yy; 45 a/=10; 46 xx=a%100; 47 a/=100; 48 yy=a; 49 return Node(xx,yy); 50 } 51 bool vis[32][32]; 52 int step[32][32]; 53 Node q[32*32]; 54 inline int bfs(Node S,Node T){ 55 if(!S.limit() || !T.limit())return INF; 56 57 memset(vis,0,sizeof(vis)); 58 59 int front,rear; 60 front=rear=0; 61 62 q[rear++]=S; 63 vis[S.x][S.y]=1; 64 step[S.x][S.y]=0; 65 while(front<rear){ 66 Node &bg=q[front]; 67 if(bg==T)return step[bg.x][bg.y]; 68 69 for(int j=0;j<4;j++){ 70 Node a=Node(bg.x+dx[j],bg.y+dy[j]); 71 if(a.limit() && !vis[a.x][a.y]){ 72 73 /*if(S==Node(3,1)&&T==Node(1,1)){ 74 printf("%d",stable[a.x][a.y]); 75 //a.write(); 76 }*/ 77 78 step[a.x][a.y]=step[bg.x][bg.y]+1; 79 vis[a.x][a.y]=1; 80 q[rear++]=a; 81 } 82 } 83 front++; 84 } 85 return INF; 86 } 87 88 inline void Spfa_init(){ 89 for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){ 90 if(stable[i][j]){ 91 stable[i][j]=0; 92 93 for(int k=0;k<4;k++) 94 { 95 Node a=Node(i+dx[k],j+dy[k]); 96 if(a.limit())add(get_hash(i,j,k),get_hash(a.x,a.y,(k+2)%4),1); 97 98 for(int l=k+1;l<4;l++){ 99 Node S=Node(i+dx[k],j+dy[k]); 100 Node T=Node(i+dx[l],j+dy[l]); 101 if(!S.limit() || !T.limit())continue; 102 103 int t=bfs(S,T); 104 //if(S==Node(3,1)&&T==Node(1,1))cout<<i<<j<<endl; 105 if(t!=INF) 106 //建图:(x,y,z)表示目标格子在(x,y)空白格子在z方向,向4个方向建边 107 { 108 add(get_hash(i,j,k),get_hash(T.x,T.y,(l+2)%4),t+1); 109 add(get_hash(i,j,l),get_hash(S.x,S.y,(k+2)%4),t+1); 110 } 111 112 113 } 114 } 115 116 stable[i][j]=1; 117 } 118 } 119 } 120 121 inline int bfs_sec(Node &E,Node S){ 122 memset(vis,0,sizeof(vis)); 123 124 int front,rear; 125 front=rear=0; 126 127 q[rear++]=E; 128 vis[E.x][E.y]=1; 129 step[E.x][E.y]=0; 130 131 while(front<rear){ 132 Node &bg=q[front]; 133 134 for(int j=0;j<4;j++){ 135 Node a=Node(bg.x+dx[j],bg.y+dy[j]); 136 137 if(a==S){ 138 E=bg; 139 //E.write(); 140 141 return step[bg.x][bg.y]; 142 } 143 144 if(a.limit() && !vis[a.x][a.y]){ 145 step[a.x][a.y]=step[bg.x][bg.y]+1; 146 vis[a.x][a.y]=1; 147 q[rear++]=a; 148 } 149 } 150 front++; 151 } 152 return INF; 153 } 154 int que[400000],dis[400000]; 155 bool inqueue[40000]; 156 inline int spfa(int ss,Node T){ 157 /*知道自己错在哪里了: 158 我的想法是 一开始让空白块跑到目标块旁边 159 但是这种方法会有漏洞,就是这次的路径可能以后的最优解会路过 160 161 也就是起点不在图里,需要从起点的上下左右跑一边再计算最短路 162 */ 163 memset(dis,0x3f,sizeof(dis)); 164 int ans=INF; 165 166 int front,rear; 167 front=rear=0; 168 169 que[rear++]=ss; 170 dis[ss]=0; 171 inqueue[ss]=1; 172 173 while(front<rear){ 174 int &bg=que[front]; 175 //printf("%d:\n",bg); 176 if(get_Node(que[front])==T)ans=min(ans,dis[bg]); 177 178 for(int i=fst[bg];i;i=nxt[i]){ 179 int v=to[i]; 180 //printf(" v%d ",v); 181 int tt=dis[bg]+w[i]; 182 //printf("dis%d\n",tt); 183 184 if(tt<dis[v]){ 185 dis[v]=tt; 186 if(!inqueue[v]){ 187 que[rear++]=v; 188 inqueue[v]=1; 189 } 190 } 191 192 } 193 194 inqueue[bg]=0; 195 front++; 196 } 197 return ans; 198 } 199 inline void solve(){ 200 if(s==t){ 201 printf("0\n"); 202 return; 203 } 204 stable[s.x][s.y]=0; 205 206 int ans=INF; 207 for(int i=0;i<4;i++){ 208 int a=spfa(get_hash(s.x,s.y,i),t); 209 if(a==INF)continue; 210 211 int b=bfs(Node(s.x+dx[i],s.y+dy[i]),e); 212 if(b==INF)continue; 213 214 ans=min(ans,a+b); 215 } 216 stable[s.x][s.y]=1; 217 218 if(ans==INF)printf("-1\n"); 219 else printf("%d\n",ans); 220 } 221 int main(){ 222 //freopen("puzzle.in","r",stdin); 223 //freopen("puzzle.out","w",stdout); 224 scanf("%d%d%d",&n,&m,&Q); 225 for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){ 226 int _x;scanf("%d",&_x);stable[i][j]=_x; 227 } 228 Spfa_init(); 229 230 for(int i=1;i<=Q;i++){ 231 e.read();//e.write(); 232 s.read();//s.write(); 233 t.read();//t.write(); 234 solve(); 235 //if(i<q)memset(vis,0,sizeof(vis)); 236 } 237 return 0; 238 } 239 /* 240 3 3 1 241 1 1 1 242 1 0 1 243 1 1 1 244 3 1 1 1 1 2 245 */