NOI 2011
暴力O(n)递推就有75。。。记下模k的结果和模g的结果,遇到模k等于1两个都减一。
注意滚动。
CODE:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn=1e7+10; long long f[5],g[5]; long long n,k,mod; int main() { scanf("%lld%lld%lld",&n,&k,&mod); f[1]=f[2]=1; g[1]=g[2]=1; for(long long i=3;i<=n;i++) { f[3]=(f[1]+f[2])%k; g[3]=(g[1]+g[2])%mod; if(f[3]%k==1) g[3]--,f[3]=0; g[3]=(g[3]+mod)%mod; g[1]=g[2],g[2]=g[3]; f[1]=f[2],f[2]=f[3]; } if(n<3) g[3]=g[1]; printf("%lld",g[3]%mod); return 0; }
很精彩的一道题。
可以发现所有的拐点都在矩形的四个角,有DP:
这个DP用spfa来转移,只不过在扩展队首的时候,要判断是否能够到达。
现在就变成了如何在最多O(n)时间内判断两点是否可以直接互相达到。
对于每个点,扩展的时候,维护一个high,一个low,表示张角的限制,因为已经按照x排过序了,所以通过叉积的计算就可以更新啦。
注意high只能用矩形的2个上角更新,low同理。
判断的时候也用叉积。
CODE:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include <cmath> using namespace std; const int maxn=9003; struct Line { double x,y; }line[maxn],s,t; int n,st,vis[maxn]; double v,dis[maxn]; double DIS(Line a,Line b) { return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)); } double cross(Line o,Line a,Line b) { double x1=a.x-o.x,y1=a.y-o.y; double x2=b.x-o.x,y2=b.y-o.y; return x1*y2-x2*y1; } bool check(int up,int low,Line a,Line b) { return !((up && cross(a,line[up],b)>0) || (low && cross(a,line[low],b)<0)); } inline void spfa() { queue<int> q; for(int i=st;i<=n;i++) dis[i]=1e9; dis[st]=0; vis[st]=1; q.push(st); while(!q.empty()) { int tt=q.front(); q.pop(); vis[tt]=0; int up=0,low=0; for(int i=tt+1;i<=n;i++) { if(check(up,low,line[tt],line[i])) { if(dis[tt]+DIS(line[tt],line[i])<dis[i]) { dis[i]=dis[tt]+DIS(line[tt],line[i]); if(!vis[i]) vis[i]=1,q.push(i); } } if(((i-1)%4+1) & 1 && (!up || cross(line[tt],line[up],line[i])<=0)) up=i; else if(!(((i-1)%4+1)&1) && (!low || cross(line[tt],line[low],line[i])>=0)) low=i; if(up && low && cross(line[tt],line[up],line[low])>0) break; } } } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%lf%lf%lf%lf",&line[i*4-2].x,&line[i*4-2].y,&line[i*4-1].x,&line[i*4-1].y); line[i*4-3]=(Line){line[i*4-2].x,line[i*4-1].y}; line[i*4]=(Line){line[i*4-1].x,line[i*4-2].y}; } n=n*4; scanf("%lf%lf",&s.x,&s.y); scanf("%lf%lf",&t.x,&t.y); scanf("%lf",&v); if(s.x>t.x) swap(s,t); for(;n;n--) if(line[n].x<=t.x) break; line[++n]=t; for(st=1;st<=n;st++) if(line[st].x>=s.x) break; line[--st]=s; spfa(); printf("%.10lf",dis[n]/v); return 0; }
搜索75/ 非常精彩的一道题
以空格为起点将棋盘黑白染色,将空格染成黑色,这样相邻两个点的颜色总是不一样的,等效于在二分图的两侧来回走。
那么一般的,任意给一个棋局,都可以看成一个已经走过一部分的二分图(不可能走重复的格子,因为是黑白交替走),考虑给定二分图先手是否必胜。
二者相当于走一个交错路(最后一步一定是先手走),由于最大匹配的交错路可以使得后手无路可走,所以我们开始关注最大匹配。
如果此图中的最大匹配独一无二,先手必胜;
不然就有可能被后手引上"歧途",从而无路可走。
如何判断一个点是否是非必需点?
- 如果当前点本来就没有匹配的点,显然不在。
- 如果当前点所匹配的点在删去当前点后,能够继续增广,就说明当前点不一定在最大匹配上,否则一定在。
好好意会吧。。。
CODE:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> using namespace std; const int maxn=50003; const int maxm=5e6+10; struct point { int x,y; int to,nxt; }edge[maxm]; int n,m,k,bx,by,cnt,tot; int map[203][203],ban[maxn],match[maxn]; int vis[maxn],viss[203][203],head[maxn],ans[maxn]; char ss[maxn]; int derx[]={0,0,0,1,-1}; int dery[]={0,1,-1,0,0}; inline void add(int u,int v) { tot++; edge[tot].nxt=head[u]; edge[tot].to=v; head[u]=tot; } inline int iu(int x,int y) { return (x-1)*m+y; } inline void Bfs() { queue<point> q; memset(viss,0,sizeof(viss)); q.push((point){bx,by,0,0}); while(!q.empty()) { point e=q.front(); q.pop(); for(int i=1;i<=4;i++) { int xx=e.x+derx[i],yy=e.y+dery[i]; if(xx<1 || xx>n || yy<1 || yy>m || !(map[e.x][e.y]^map[xx][yy])) continue; add(iu(e.x,e.y),iu(xx,yy)); add(iu(xx,yy),iu(e.x,e.y)); if(!viss[xx][yy]) { q.push((point){xx,yy,0,0}); viss[xx][yy]=1; } } } } inline bool dfs(int x) { for(int i=head[x];i;i=edge[i].nxt) { int v=edge[i].to; if(vis[v] || ban[v]) continue; vis[v]=1; if(!match[v] || dfs(match[v])) { match[v]=x; match[x]=v; return true; } } return false; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { scanf("%s",ss+1); for(int j=1;j<=m;j++) { if(ss[j]=='.') { bx=i,by=j; map[i][j]=1; } else if(ss[j]=='X') map[i][j]=1; else if(ss[j]=='O') map[i][j]=0; } } Bfs(); for(int i=1;i<=n*m;i++) if(!match[i]) { memset(vis,0,sizeof(vis)); dfs(i); } scanf("%d",&k); for(int i=1;i<=k;i++) { int cn=iu(bx,by),a1=0,a2=0; ban[cn]=1; if(match[cn]) { int op=match[cn]; match[op]=0; match[cn]=0; memset(vis,0,sizeof(vis)); a1=!dfs(op); } else a1=0; scanf("%d%d",&bx,&by); cn=iu(bx,by); ban[cn]=1; if(match[cn]) { int op=match[cn]; match[op]=0; match[cn]=0; memset(vis,0,sizeof(vis)); a2=!dfs(op); } else a2=0; if(a1 && a2) ans[++cnt]=i; scanf("%d%d",&bx,&by); } printf("%d\n",cnt); for(int i=1;i<=cnt;i++) printf("%d\n",ans[i]); return 0; }
一遍dfs就行了,NOIP之前做的题 o_O
//long long 害死人 #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> using namespace std; const int maxn=1e6+10; struct point { int to; long long w; int nxt; }edge[maxn<<1]; int n,tot; int head[maxn],S[maxn]; long long ans=0; inline void add(int u,int v,long long w) { tot++; edge[tot].to=v; edge[tot].nxt=head[u]; edge[tot].w=w; head[u]=tot; } inline void dfs(int x,int fa) { S[x]=1; for(int i=head[x];i;i=edge[i].nxt) { int v=edge[i].to; if(v!=fa) { dfs(v,x); ans+=(long long)abs(n-S[v]*2)*edge[i].w; S[x]+=S[v]; } } } int main() { cin>>n; for(int i=1;i<=n-1;i++) { int a,b; long long w; scanf("%d%d%lld",&a,&b,&w); add(a,b,w); add(b,a,w); } dfs(1,0); printf("%lld",ans); return 0; }