D Tablecity
从一头往另一头扫一遍,再逆着回来就好了。因为贼每走一步,横坐标的奇偶性都会改变。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; int i; int main(){ printf("2000\n"); for (i=1;i<=1000;i++) printf("%d 1 %d 2\n",i,i); for (i=1000;i>0;i--) printf("%d 1 %d 2\n",i,i); return 0; }
H Bots
每走一步,树增加了一层,节点数是上一层的2倍。但是到了第n+1层开始,就会出现红/蓝边数量达到n的情况,这样的节点不能翻倍,计算一下组合数把这种情况减去就行了。算组合数的时候递推计算,用到了逆元。
#include <iostream> #include <cstdio> #include <algorithm> #include <set> #include <vector> #include <string.h> using namespace std; #define ll long long const int mod=1000000007; void ExEuclid(ll a,ll b,ll &x,ll &y,ll &q){ if(b==0){ x=1;y=0;q=a; return; } ExEuclid(b,a%b,y,x,q); y-=x*(a/b); } ll inv(ll num){ ll x,y,q; ExEuclid(num,mod,x,y,q); if(q==1)return (x+mod)%mod; } int main(){ int n; while(cin>>n){ ll ans=1; ll cur=1; ll C=1; for(int i=1;i<=n*2;i++){ cur*=2; cur%=mod; if(i>n){ cur-=2*C; cur+=mod; cur+=mod; cur%=mod; C*=i; C%=mod; C*=inv(i-n); C%=mod; } ans+=cur; ans%=mod; } cout<<ans<<endl; } return 0; }
F Bulbo
首先有一个可以推出来的结论,你只移动到输入数据中出现的那些位置,可以得到最优解(不需要移动到数据中没有出现的位置去)。所以就可以用离散化+dp+滚动数组来解决了。dp(i,j)表示第i轮时,位置在j(离散化后)的最优解(第一维滚动只开2)。注意状态转移时,本应是多个状态转移到一个状态,这样复杂度太高会超时,需要利用递推维护多个状态的最优值,一次性转移到一个状态。
顺便说一下,后来发现这个题可以贪心,每次维护一个范围,每次开始前移动到这个范围内,灯亮,花费为最少。
#include <iostream> #include <cstdio> #include <algorithm> #include <set> #include <vector> #include <string.h> using namespace std; #define ll long long const int mod=1000000007; ll INF=1000000000000000000LL; int l[5010]; int r[5010]; ll dp[2][100010]; int pos[100010]; int k; int main(){ int n,x; while(cin>>n>>x){ memset(dp,-1,sizeof(dp)); ll ans=INF; k=0; pos[k++]=x; for(int i=1;i<=n;i++){ scanf("%d%d",&l[i],&r[i]); pos[k++]=l[i]; pos[k++]=r[i]; } sort(pos,pos+k); k=unique(pos,pos+k)-pos; for(int i=0;i<k;i++){ dp[0][i]=abs(x-pos[i]); } for(int i=1;i<=n;i++){ ll part2=INF; for(int j=0;j<k;j++){ dp[i%2][j]=INF; ll part1=0; if(pos[j]<l[i]){ part1=l[i]-pos[j]; }else if(pos[j]>r[i]){ part1=pos[j]-r[i]; } if(j>0){ part2=min(part2+pos[j]-pos[j-1],dp[(i-1)%2][j]); }else{ part2=dp[(i-1)%2][j]; } dp[i%2][j]=min(dp[i%2][j],part1+part2); } part2=INF; for(int j=k-1;j>=0;j--){ ll part1=0; if(pos[j]<l[i]){ part1=l[i]-pos[j]; }else if(pos[j]>r[i]){ part1=pos[j]-r[i]; } if(j<k-1){ part2=min(part2+pos[j+1]-pos[j],dp[(i-1)%2][j]); }else{ part2=dp[(i-1)%2][j]; } dp[i%2][j]=min(dp[i%2][j],part1+part2); } } for(int i=0;i<k;i++){ ans=min(ans,dp[n%2][i]); } cout<<ans<<endl; } return 0; }
B Bribes
首先为了迅速求得树上许多点对之间的距离,需要使用nlogn的在线LCA(倍增法)。然后需要知道每条单向边被逆行了多少次,这时可用打标记的方法,起点+1终点-1,又由于它是树型结构,统计时用dfs。最后的计算利用了等比数列求和公式和快速幂。
#include <iostream> #include <cstdio> #include <algorithm> #include <set> #include <vector> #include <string.h> using namespace std; #define ll long long const int mod=1000000007; ll INF=1000000000000000000LL; #define max_size 100010 ll Quick_Pow(ll a,ll n){ ll ret=1; ll temp=a%mod; while (n){ if (n&1) ret=ret*temp%mod; temp=temp*temp%mod; n>>=1; } return ret; } int d[max_size],p[max_size][18]; int head[max_size]; int cnt; int a[max_size]; int b[max_size]; int x[max_size]; bool downpoint[max_size]; bool uppoint[max_size]; int downres[max_size]; int upres[max_size]; bool vis2[max_size]; struct Edge{ int v; int pre; }eg[max_size]; void add(int x,int y){ eg[cnt].v=y; eg[cnt].pre=head[x]; head[x]=cnt++; } void dfs(int k){ int m,x,i,j; for(i=head[k];i!=0;i=eg[i].pre){ x=eg[i].v; p[x][0]=k; m=k; d[x]=d[k]+1; for(j=0;p[m][j]!=0;j++){ p[x][j+1]=p[m][j]; m=p[m][j]; } dfs(x); } } int find_lca(int x,int y){ int m,k; if(x==y)return x; if(d[x]<d[y]){ m=x; x=y; y=m; } m=d[x]-d[y]; k=0; while(m){ if(m&1) x=p[x][k]; m>>=1; k++; } if(x==y)return x; k=0; while(x!=y){ if(p[x][k]!=p[y][k]||p[x][k]==p[y][k]&&k==0){ x=p[x][k]; y=p[y][k]; k++; } else{ k--; } } return x; } struct oriEdge{ int v; int flag; oriEdge(int v,int flag):v(v),flag(flag){ } oriEdge(){ } }; vector<oriEdge> E[max_size]; bool vis[max_size]; int siz[max_size]; int predfs(int u){ vis[u]=1; int sz=E[u].size(); siz[u]=1; for(int i=0;i<sz;i++){ int v=E[u][i].v; if(!vis[v]){ if(E[u][i].flag==0){ downpoint[v]=1; } if(E[u][i].flag==1){ uppoint[v]=1; } add(u,v); siz[u]+=predfs(v); } } return siz[u]; } ll ans=0; int downcnt[max_size]; int upcnt[max_size]; void dfs2(int k){ downcnt[k]+=downres[k]; upcnt[k]+=upres[k]; for(int i=head[k];i!=0;i=eg[i].pre){ int x=eg[i].v; dfs2(x); downcnt[k]+=downcnt[x]; upcnt[k]+=upcnt[x]; } if(uppoint[k]){ ans+=Quick_Pow(2,upcnt[k])-1; ans+=mod; ans%=mod; } if(downpoint[k]){ ans+=Quick_Pow(2,downcnt[k])-1; ans+=mod; ans%=mod; } } int main(){ int n; while(cin>>n){ cnt=1; for(int i=1;i<n;i++){ scanf("%d%d%d",&a[i],&b[i],&x[i]); if(!x[i])E[a[i]].push_back(oriEdge(b[i],2)); else E[a[i]].push_back(oriEdge(b[i],1)); if(!x[i])E[b[i]].push_back(oriEdge(a[i],2)); else E[b[i]].push_back(oriEdge(a[i],0)); } predfs(1); dfs(1); int k; cin>>k; int Last=1; for(int i=1;i<=k;i++){ int s; scanf("%d",&s); int LCA=find_lca(Last,s); ll cur=0; if(Last!=LCA){ upres[Last]++; upres[LCA]--; } if(s!=LCA){ downres[s]++; downres[LCA]--; } Last=s; } dfs2(1); cout<<ans<<endl; } return 0; }
G Run for beer
注意题目给的数据范围,边的长度最长是9,但是每多经过一个点,速度变为1/10。也就是说,需要经过尽可能少的点。另外,边长度可以是0,也就是说,如果最后经过了一系列长度为0的边,是不会增加时间的。我的做法是多次bfs,先找出哪些点到终点可以全是0边,然后在这些点中,找一个离起点最近的,再贪心找到路径。实现比较挫,bfs了好多次,也跪了好多次,最后才过。。。
#include <iostream> #include <cstdio> #include <algorithm> #include <set> #include <stack> #include <vector> #include <queue> #include <string.h> using namespace std; #define ll long long const int INF = 1000000000; const int maxm=100010; const int maxn=100010; int head[maxn]; int pre[maxm<<1]; int from[maxm<<1]; int to[maxm<<1]; int len[maxm<<1]; int vis[maxn]; int tote; int k; int lv[maxn]; int _lv[maxn]; bool finish[maxn]; bool finish2[maxn]; int cost[maxn]; int lvcost[maxn]; int fa[maxn]; int _fa[maxn]; void init(){ memset(head,-1,sizeof(head)); tote=0; k=0; } void addedge(int u,int v,int l){ to[tote]=v; from[tote]=u; len[tote]=l; pre[tote]=head[u]; head[u]=tote++; // to[tote]=u; from[tote]=v; len[tote]=l; pre[tote]=head[v]; head[v]=tote++; } int ans[maxn]; int path[maxn]; void clearQueue(queue<int> &que){ while(que.size())que.pop(); } int print(int u){ int re=0; if(!finish[u])printf("%d",cost[u]); if(u)re=print(from[fa[u]])+1; path[k++]=u; return re; } int main(){ init(); int n,m; cin>>n>>m; for(int i=1;i<=m;i++){ int u,v,l; scanf("%d%d%d",&u,&v,&l); addedge(u,v,l); } queue<int> que; que.push(0); vis[0]=1; while(que.size()){ int u=que.front(); que.pop(); for(int i=head[u];~i;i=pre[i]){ int v=to[i]; if(vis[v])continue; vis[v]=1; lv[v]=lv[u]+1; que.push(v); } } clearQueue(que); memset(vis,0,sizeof(vis)); que.push(n-1); vis[n-1]=1; while(que.size()){ int u=que.front(); que.pop(); finish[u]=1; for(int i=head[u];~i;i=pre[i]){ int v=to[i]; if(len[i])continue; //only 0 edge if(vis[v])continue; vis[v]=1; _lv[v]=_lv[u]+1; que.push(v); _fa[v]=i; } } int MIN=INF; for(int i=0;i<n;i++){ if(finish[i]){ MIN=min(MIN,lv[i]); } } for(int i=0;i<n;i++){ if(lv[i]!=MIN){ finish[i]=0; } } clearQueue(que); memset(vis,0,sizeof(vis)); for(int i=0;i<n;i++){ lvcost[i]=INF; if(finish[i]){ que.push(i); vis[i]=1; }else{ cost[i]=INF; } } lvcost[MIN]=0; while(que.size()){ int u = que.front(); que.pop(); if(lvcost[lv[u]]!=cost[u])continue; for(int i=head[u];~i;i=pre[i]){ int v=to[i]; if(lv[v]+1!=lv[u])continue; cost[v]=min(cost[v],len[i]); lvcost[lv[v]]=min(lvcost[lv[v]],cost[v]); if(vis[v])continue; vis[v]=1; que.push(v); } } clearQueue(que); memset(vis,0,sizeof(vis)); que.push(0); vis[0]=1; while(que.size()){ int u=que.front(); que.pop(); if(finish[u]){ finish2[u]=1; } for(int i=head[u];~i;i=pre[i]){ if(len[i]!=cost[u])continue; int v=to[i]; if(vis[v])continue; vis[v]=1; que.push(v); fa[v]=i; } } int MIN2=INF; int target=-1; for(int i=0;i<n;i++){ if(finish2[i]){ if(_lv[i]<MIN2){ MIN2=_lv[i]; target=i; } } } if(print(target)==0){ printf("0"); } printf("\n"); int cur=target; if(cur!=n-1)while(1){ cur=from[_fa[cur]]; path[k++]=cur; if(cur==n-1)break; } cout<<k<<endl; for(int i=0;i<k;i++){ printf("%d ",path[i]); } return 0; }