NOIP2017Day1题解
Day1
T1.小学奥数。。。
代码:
#include<iostream> #include<cstring> #include<string> #include<algorithm> #include<cstdio> #include<cmath> using namespace std; long long a,b; int main() { scanf("%lld%lld",&a,&b); long long ans=a*b-a-b; printf("%lld",ans); return 0; }
T2:大模拟题,没什么好讲的(我还写的特别麻烦,估计最傻逼的代码)
代码:
#include<iostream> #include<cstring> #include<string> #include<algorithm> #include<cstdio> #include<cmath> #define INF 1000 using namespace std; int T,l; bool vis[1005],vis1[10005]; char s[10005],ch[105][10]; bool c[10005],e[1005]; int d[1005]; char ssss[1005],sss[10005],ss[105]; int a[1005],b[1005]; int main() { scanf("%d",&T); while (T--){ bool flag=true; int sum=0; bool zong=true; memset(a,0,sizeof(a)); memset(b,0,sizeof(b)); scanf("%d%s",&l,s+1); int n=strlen(s+1); if (s[3]=='n') flag=true; else flag=false; if (flag){ int ckr=0; for (int i=1;i<=n;i++){ if (s[i]=='^'){ ckr=i; break; } } for (int i=ckr+1;i<=n;i++){ if (s[i]>='0'&&s[i]<='9') sum=sum*10+s[i]-'0'; else break; } } else { int ckr=0; for (int i=1;i<=n;i++){ if (s[i]=='('){ ckr=i; break; } } for (int i=ckr+1;i<=n;i++){ if (s[i]>='0'&&s[i]<='9') sum=sum*10+s[i]-'0'; else break; } } if (!flag) sum=0; for (int i=1;i<=l;i++){ scanf("%s",ss); if (ss[0]=='F'){ cin>>ch[i][0]; //scanf("%s",ch[i][0]); scanf("%s%s",sss+1,ssss+1); if (sss[1]=='n') a[i]=INF; else { int n=strlen(sss+1); for (int j=1;j<=n;j++) a[i]=a[i]*10+sss[j]-'0'; } if (ssss[1]=='n') b[i]=INF; else { int n=strlen(ssss+1); for (int j=1;j<=n;j++) b[i]=b[i]*10+(ssss[j]-'0'); } if (a[i]==INF&&b[i]==INF) d[i]=0; else if (a[i]>b[i]) d[i]=-1; else if (a[i]==INF&&b[i]<INF) d[i]=-1; else if (a[i]<INF&&b[i]<INF) d[i]=0; else if (a[i]<INF&&b[i]==INF) d[i]=1; c[i]=true; } else c[i]=false; } memset(vis,false,sizeof(vis)); memset(vis1,false,sizeof(vis1)); int i=1; int j=1; int tot=0; int bianlitot=0; int Max1=0; int flag1=0; while (j<=l){ if (c[j]==true){ if (vis[ch[j][0]-'a']){ if (zong) puts("ERR"); zong=false; } vis[ch[j][0]-'a']=true; if (flag1==0){ if (d[j]==1) tot++,e[j]=true; else if (d[j]==0) tot=tot,e[j]=true; else if (d[j]==-1) flag1++,e[j]=false; } else{ tot=tot; e[j]=false; if (d[j]==-1) flag1++; } bianlitot++; Max1=max(Max1,tot); } else { bianlitot--; if (bianlitot<0){ if (zong) puts("ERR"); zong=false; } for (int k=j;k>=1;k--){ if (c[k]==true&&vis1[k]==false){ vis[ch[k][0]-'a']=false; vis1[k]=true; if (e[k]) tot=tot-d[k]; else if (e[k]==false){ tot=tot; if (d[k]==-1) flag1--; } break; } } Max1=max(Max1,tot); } j++; } if (!zong) continue; if (bianlitot!=0) puts("ERR"); else{ if (Max1==sum) puts("Yes"); else puts("No"); } } return 0; }
T3.一看k<=50,很显然的状态就有了
我们考虑用dp[i][j]表示到i这个点浪费了的代价为j,这里浪费的代价可以这么理解:就是到i相比于最短路dis[i]来说长了j,也就是说走了dis[i]+j的路
考虑转移dp[i][j]->dp[v][j+dis[i]+e[i].step-dis[v]](当前走得路径+到v的长度和到v的最短路进行比较,就知道了浪费了多少),其中dis[i]表示到i的最短路
注意更新顺序,关于dis排序来更新
另外DP的时候要先枚举差量,一定是差量小的来更新差量大的
这样就有70分了
我们考虑怎么拿满分。
注意到一个套路,就是如果拓扑排序后有点剩余,那么剩下的点一定构成环
然后对于-1的情况就是有一个0环在路径上
我们用拓扑排序判一下就行了
同一个 0 环上任意一个点到1的最短路和到n的最短路都一样
所以当这个点 i 满足 dis1[i]+disn[i]<=dis1[n]+K 时,就可以输出 -1了
代码:
#include<bits/stdc++.h> #define N 200005 #define M 100005 using namespace std; int T,n,m,K,Mod,kk,kkk,kkkk,x,y,z; int head[M],head1[M],Head[N],dis1[M],dis2[N],ru[M],dp[M][55],tmp[M],q[N*100]; struct Edge{int nxt,to,step;}e[N]; struct Edge1{int nxt,to,step;}e1[N]; struct Edge2{int nxt,to;}E[N]; inline void link(int x,int y,int z){e[++kk].nxt=head[x];e[kk].to=y;e[kk].step=z;head[x]=kk;} inline void link1(int x,int y,int z){e1[++kkk].nxt=head1[x];e1[kkk].to=y;e1[kkk].step=z;head1[x]=kkk;} inline void Link(int x,int y){E[++kkkk].nxt=Head[x];E[kkkk].to=y;Head[x]=kkkk;} inline void spfa1(){ memset(dis1,127,sizeof(dis1));dis1[1]=0; int left1=1;int right1=1;q[1]=1; while (left1<=right1){ int u=q[left1++]; for (int i=head[u];i;i=e[i].nxt){ int v=e[i].to; if (dis1[v]>dis1[u]+e[i].step){ dis1[v]=dis1[u]+e[i].step; q[++right1]=v; } } } } inline void spfa2(){ memset(dis2,127,sizeof(dis2));dis2[n]=0; int left1=1;int right1=1;q[1]=n; while (left1<=right1){ int u=q[left1++]; for (int i=head1[u];i;i=e1[i].nxt){ int v=e1[i].to; if (dis2[v]>dis2[u]+e1[i].step){ dis2[v]=dis2[u]+e1[i].step; q[++right1]=v; } } } } int id[N]; inline bool cmp(int x,int y){if(dis1[x]==dis1[y])return id[x]<id[y];else return dis1[x]<dis1[y];} inline void solve(){ memset(dp,0,sizeof(dp));dp[1][0]=1; for (int i=1;i<=n;i++) tmp[i]=i; sort(tmp+1,tmp+n+1,cmp); for (int k=0;k<=K;k++){//注意先枚举差量,一定是差量小的先更新差量大的 for (int i=1;i<=n;i++){ int u=tmp[i]; for (int j=head[u];j;j=e[j].nxt){ int v=e[j].to; if (k+dis1[u]+e[j].step-dis1[v]>K) continue; dp[v][k+(dis1[u]+e[j].step-dis1[v])]=1ll*(dp[v][k+(dis1[u]+e[j].step-dis1[v])]+dp[u][k])%Mod; } } } int ans=0; for (int i=0;i<=K;i++) ans=(ans+dp[n][i])%Mod; if (!ans) puts("-1");else printf("%d\n",ans); } bool check(){//拓扑排序找环,如果存在一个环,那么这个环中的点都不会加入到拓扑序中,显然不可能会入度为0 int left1=1;int right1=0;//right1=0打成right1=1了 for (int i=1;i<=n;i++) if (!ru[i]) q[++right1]=i,id[i]=n; while (left1<=right1){ int u=q[left1++]; for (int i=Head[u];i;i=E[i].nxt){ int v=E[i].to; if (!--ru[v]) q[++right1]=v; } } for (int i=1;i<=right1;i++) id[q[i]]=i; for (int i=1;i<=n;i++) if (ru[i]&&dis1[i]+dis2[i]<=dis1[n]+K) return true;//如果i这个环在可能的路上那么就gg return false; } inline void init(){ memset(Head,0,sizeof(Head));kkkk=0; memset(head,0,sizeof(head));kk=0; memset(head1,0,sizeof(head1));kkk=0; memset(ru,0,sizeof(ru)); } int main(){ scanf("%d",&T); while (T--){ init(); scanf("%d%d%d%d",&n,&m,&K,&Mod); for (int i=1;i<=m;i++){ scanf("%d%d%d",&x,&y,&z); link(x,y,z);link1(y,x,z); if (!z) Link(x,y),ru[y]++; } spfa1();spfa2(); if (check()) puts("-1"); else solve(); } return 0; } /*1 2 2 6 5 1 2 2 2 1 2*/