NOIP模拟测试6

看题目就知道这是一个悲伤的故事。。。

但还有更悲伤的

考崩了,难以描述。

T1把数据范围看成2^12,我TM也是够了。。。

T2思路接近正解,但不知道想了个神魔东西跑了N遍dijstra

T3最狗了,暴力二十分没拿到,因为我打的贪心。。

T1:很水的DP+组合数学

DP转移显然:f[i][j]=sum[i-1][j-1]-sum[i-1][j-m];
但是这样时空复杂度都是(N*d)
但期望复杂度是(N^2)
考虑如何优化
发现f[i][j]实际上有许多零出现,但我们还把它当成有用的状态转移了,因此考虑抹去这些冗余。
用组合数学,f[i][j]的定义改为送出礼物的i天              注意取模!



AC代码:
 1 #include<bits/stdc++.h>
 2 #define MAXN 2005
 3 #define mem(a) memset(a,0,sizeof(a))
 4 using namespace std;
 5 const long long mod=998244353;
 6 long long C[MAXN],inv[MAXN];
 7 inline long long Rd()
 8 {
 9     long long x=0;char c=getchar();long long f=1;
10     while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
11     while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
12     return x*f;
13 }
14 long long n,d,m;
15 long long f[MAXN][MAXN],sum[MAXN][MAXN];
16 inline long long qpower(long long a,long long b)
17 {
18     long long ans=1;
19     while(b)
20     {
21         if(b&1)ans=ans*a%mod;
22         a=a*a%mod;
23         b>>=1;
24     }
25     return ans%mod;
26 }
27 inline void Get_C()
28 {
29     C[1]=d%mod;
30     for(long long i=2;i<=n;i++)
31     {
32         C[i]=C[i-1]*inv[i]%mod*((d-i+1)%mod)%mod;
33     }
34 }
35 int main()
36 {
37     //freopen("out.in","r",stdin);
38     //freopen("a.txt","w",stdout);
39     for(long long i=1;i<=2000;i++)inv[i]=qpower(i,mod-2);
40     while(1)
41     {
42         mem(sum);mem(f);
43         n=Rd();d=Rd();m=Rd();
44         long long ans=0;
45         Get_C();
46         if(n==0&&d==0&&m==0)return 0;
47         for(long long i=1;i<m;i++)f[1][i]=1,sum[1][i]=sum[1][i-1]+f[1][i];
48         for(long long i=m;i<=n;i++)sum[1][i]=sum[1][m-1];
49         for(long long i=2;i<=n;i++)
50         {
51             for(long long j=0;j<=n;j++)
52             {
53                 if(j>=m)f[i][j]=(mod+sum[i-1][j-1]-sum[i-1][j-m])%mod;
54                 else f[i][j]=sum[i-1][j-1];
55                 sum[i][j]=(sum[i][j-1]+f[i][j])%mod;
56             }
57         }
58         for(int i=1;i<=n;i++)ans=(ans+C[i]*f[i][n]%mod)%mod;
59         cout<<ans%mod<<endl;
60     }
61     return 0;
62 }
View Code

T2:找最小环

想了一个**算法,时间复杂度O(n^2log(n))空间复杂度(n^2)

  1 #include<cstdio>
  2 #include<queue>
  3 #include<bits/stdc++.h>
  4 #define ts puts("---------------");
  5 #define MAXN 20005
  6 #define mem(a) memset(a,0,sizeof(a))
  7 using namespace std;
  8 int head[MAXN],nxt[MAXN],to[MAXN],cnt,s[MAXN],top,dfn[MAXN],low[MAXN],ans,tot,v[MAXN],val[MAXN];
  9 bool yes_get,vst[1005],in_s[MAXN],Fail[MAXN*10];
 10 int edge[2005][2005],d[1005],di[1005],pre[1005];
 11 void clear()
 12 {
 13     mem(head);mem(s);mem(dfn);mem(in_s);mem(v);mem(Fail);mem(vst);mem(pre);
 14     yes_get=ans=tot=cnt=top=0;
 15 }
 16 inline int Rd()
 17 {
 18     int x=0;char c=getchar();int f=1;
 19     while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
 20     while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
 21     return x*f;
 22 }
 23 void add(int u,int v,int w)
 24 {
 25     to[++cnt]=v;
 26     nxt[cnt]=head[u];
 27     val[cnt]=w;
 28     head[u]=cnt;
 29     return ;
 30 }
 31 void Tarjan(int x,int fa)
 32 {
 33     dfn[x]=low[x]=++tot;
 34     s[++top]=x;
 35     in_s[x]=1;
 36     for(int i=head[x];i;i=nxt[i])
 37     {
 38         int y=to[i];
 39         if(y==fa)continue;
 40         if(!dfn[y])
 41         {
 42             v[y]=val[i];
 43             Tarjan(y,x);
 44             low[x]=min(low[x],low[y]);
 45         }
 46         else 
 47         {
 48             if(in_s[y])
 49             {
 50                 if(dfn[y]<low[x])v[x]+=val[i];
 51                 low[x]=min(low[x],dfn[y]);
 52             }
 53         }
 54     }
 55 
 56     if(low[x]==dfn[x])
 57     {
 58         ans=0;
 59         while(top)
 60         {
 61             int p=s[top--];
 62             in_s[p]=0;
 63             ans+=v[p];
 64             if(p==1)yes_get=1;
 65             if(p==x)break;
 66         }
 67     }
 68     if(!yes_get||!ans)ans=-1;
 69     else return ;
 70 }
 71 int Getmin(int i)
 72 {
 73     priority_queue<pair<int,int> >Q;
 74     while(Q.size())Q.pop();
 75     memset(di,0x3f,sizeof(di));
 76     mem(vst);
 77     di[1]=0;
 78     Q.push(make_pair(0,1));
 79     while(!Q.empty())
 80     {
 81         pair<int,int>k=Q.top();Q.pop();
 82         int x=k.second;
 83         if(vst[x])continue;
 84         if(x==i)return di[x];
 85         vst[x]=1;
 86         for(int i=head[x];i;i=nxt[i])
 87         {
 88             if(Fail[i])continue;
 89             int y=to[i],va=val[i];
 90             if(va+di[x]<di[y])
 91             {
 92                 di[y]=va+di[x];
 93                 Q.push(make_pair(-di[y],y));
 94             }
 95         }
 96     }
 97     return di[0];
 98 }
 99 void pre_Get()
100 {
101     priority_queue<pair<int,int> >Q;
102     memset(d,0x3f,sizeof(d));
103     mem(vst);
104     d[1]=0;
105     Q.push(make_pair(0,1));
106     while(!Q.empty())
107     {
108         pair<int,int>k=Q.top();Q.pop();
109         int x=k.second;
110         if(vst[x])continue;
111         vst[x]=1;
112         for(int i=head[x];i;i=nxt[i])
113         {
114             int y=to[i],va=val[i];
115             if(va+d[x]<d[y])
116             {
117                 pre[y]=x;
118                 d[y]=va+d[x];
119                 Q.push(make_pair(-d[y],y));
120             }
121         }
122     }
123     return ;
124 }
125 int main()
126 {
127 //    freopen("out.in","r",stdin);
128 //    freopen("b.txt","w",stdout);
129     int t=Rd();
130     while(t--)
131     {
132         clear();
133         int n=Rd(),m=Rd();
134         if(n==m)
135         {
136             while(m--)
137             {
138                 int u=Rd(),v=Rd(),w=Rd();
139                 add(u,v,w);
140                 add(v,u,w);
141             }
142             Tarjan(1,0);
143             cout<<ans<<endl;
144         }
145         else 
146         {
147             ans=0x7f7f7f7f;
148             while(m--)
149             {
150                 int u=Rd(),v=Rd(),w=Rd();
151                 add(u,v,w);
152                 edge[u][v]=cnt;
153                 add(v,u,w);
154                 edge[v][u]=cnt;
155             }
156             pre_Get();
157             for(int i=2;i<=n;i++)
158             {
159                 if(d[i]==d[0])continue;
160                 int t=i;
161                 while(t!=1&&t!=0)
162                 {
163                     Fail[edge[pre[t]][t]]=1;
164                     Fail[edge[t][pre[t]]]=1;
165                     t=pre[t];
166                 }
167                 if(Getmin(i)!=di[0])ans=min(ans,Getmin(i)+d[i]);
168                 t=i;
169                 while(t!=1)
170                 {
171                     Fail[edge[pre[t]][t]]=0;
172                     Fail[edge[t][pre[t]]]=0;
173                     t=pre[t];
174                 }
175             }
176             cout<<(ans==0x7f7f7f7f?-1:ans)<<endl;
177         }
178     }
179     return 0;
180 }
181 /*
182 1
183 4 5
184 1 2 2
185 2 3 2
186 3 4 2
187 1 4 2
188 1 3 5
189 */
献出我的sb算法

但实际上仔细想想发现,我的算法是有许多冗余的,只需要枚举和一直接相邻的边就够了。

因此。。。

AC代码

 1 #include<cstdio>
 2 #include<queue>
 3 #include<bits/stdc++.h>
 4 #define ts puts("---------------");
 5 #define MAXN 80005
 6 #define mem(a) memset(a,0,sizeof(a))
 7 using namespace std;
 8 int head[MAXN],nxt[MAXN],to[MAXN],cnt=1,ans,val[MAXN];
 9 bool Fail[MAXN],vst[10005];
10 int di[10005];
11 void clear()
12 {
13     mem(head);mem(Fail);
14     cnt=1;
15 }
16 inline int Rd()
17 {
18     int x=0;char c=getchar();int f=1;
19     while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
20     while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
21     return x*f;
22 }
23 void add(int u,int v,int w)
24 {
25     to[++cnt]=v;
26     nxt[cnt]=head[u];
27     val[cnt]=w;
28     head[u]=cnt;
29     return ;
30 }
31 int Getmin(int i,int ww)
32 {
33     priority_queue<pair<int,int> >Q;
34     while(Q.size())Q.pop();
35     memset(di,0x3f,sizeof(di));
36     mem(vst);
37     di[1]=0;
38     Q.push(make_pair(0,1));
39     while(!Q.empty())
40     {
41         pair<int,int>k=Q.top();Q.pop();
42         int x=k.second;
43         if(di[x]+ww>ans)return di[x]+ww;
44         if(vst[x])continue;
45         if(x==i)return di[x]+ww;
46         vst[x]=1;
47         for(int i=head[x];i;i=nxt[i])
48         {
49             if(Fail[i])continue;
50             int y=to[i],va=val[i];
51             if(va+di[x]<di[y])
52             {
53                 di[y]=va+di[x];
54                 Q.push(make_pair(-di[y],y));
55             }
56         }
57     }
58     return di[0];
59 }
60 int main()
61 {
62 //    freopen("out.in","r",stdin);
63 //    freopen("b.txt","w",stdout);
64     int t=Rd();
65     while(t--)
66     {
67         clear();
68         int n=Rd(),m=Rd();
69         ans=0x3f3f3f3f;
70         while(m--)
71         {
72             int u=Rd(),v=Rd(),w=Rd();
73             add(u,v,w);
74             add(v,u,w);
75         }
76         for(int i=head[1];i;i=nxt[i])
77         {
78             int k=to[i];
79             Fail[i]=Fail[i^1]=1;
80             ans=min(ans,Getmin(k,val[i]));
81             Fail[i]=Fail[i^1]=0;
82         }
83         cout<<(ans==0x3f3f3f3f?-1:ans)<<endl;
84     }
85     return 0;
86 }
87 /*
88 1
89 4 5
90 1 2 2
91 2 3 2
92 3 4 2
93 1 4 2
94 1 3 5
95 */
View Code

LNC的A*算法(借鉴一下,无意侵权)

  1 #include<iostream>
  2 #include<cstring>
  3 #include<cstdio>
  4 #include<cstring>
  5 #include<queue>
  6 using namespace std;
  7 const int maxn=1e4+5,maxm=4e4+5,INF=0x3f3f3f3f;
  8 int T,n,m,x,y,z,dex,yes,tot=1,top,cld[maxn],yet[maxn],instack[maxn],stack[maxn],first[maxn];
  9 struct road{
 10     int u,t,w,nxt;
 11 }eage[maxm<<1];
 12 struct point1{
 13     int id,dis;
 14     bool friend operator < (const point1 a,const point1 b)
 15     {
 16         return a.dis>b.dis;
 17     }
 18 }dis[maxn];
 19 struct point2{
 20     int id,h;
 21     bool friend operator < (const point2 a,const point2 b)
 22     {
 23         return dis[a.id].dis+a.h>dis[b.id].dis+b.h;
 24     }
 25 };
 26 priority_queue<point1> q1;
 27 priority_queue<point2> q2;
 28 void add(int x,int y,int z)
 29 {
 30     eage[++tot].u=x;
 31     eage[tot].t=y;
 32     eage[tot].w=z;
 33     eage[tot].nxt=first[x];
 34     first[x]=tot;
 35 }
 36 void clear()
 37 {
 38     top=0;tot=1;
 39     memset(first,0,sizeof(first));
 40     memset(cld,0,sizeof(cld));
 41     memset(yet,0,sizeof(yet));
 42     memset(instack,0,sizeof(instack));
 43 }
 44 void dijk()
 45 {
 46     memset(yet,0,sizeof(yet));
 47     for(int i=1;i<=n;i++) dis[i].id=i,dis[i].dis=INF;
 48     dis[1].dis=0;
 49     q1.push(dis[1]);
 50     while(q1.empty()==0)
 51     {
 52         int x=q1.top().id;q1.pop();
 53         if(yet[x]) continue;
 54         yet[x]=1;
 55         for(int i=first[x];i;i=eage[i].nxt)
 56             if(dis[x].dis+eage[i].w<dis[eage[i].t].dis)
 57                 dis[eage[i].t].dis=dis[x].dis+eage[i].w,q1.push(dis[eage[i].t]);
 58     }
 59     memset(yet,0,sizeof(yet));
 60 }
 61 int work(int st)
 62 {
 63     while(q2.empty()==0) q2.pop();
 64     point2 x,tmp;
 65     x.id=st;x.h=0;
 66     q2.push(x);
 67     int ans=INF;
 68     while(q2.empty()==0)
 69     {
 70         x=q2.top();q2.pop();
 71         yet[x.id]=1;
 72         if(x.id==1)
 73         {
 74             ans=x.h;
 75             break;
 76         }
 77         for(int i=first[x.id];i;i=eage[i].nxt)
 78             if(i!=dex&&yet[eage[i].t]==0)
 79             {
 80                 tmp.id=eage[i].t;
 81                 tmp.h=eage[i].w+x.h;
 82                 q2.push(tmp);
 83             }
 84     }
 85     memset(yet,0,sizeof(yet));
 86     return ans;
 87 }
 88 void Astar()
 89 {
 90     dijk();
 91     int ans=INF;
 92     for(int i=first[1];i;i=eage[i].nxt)
 93     {
 94         dex=i^1;
 95         int t=eage[i].t,d=work(t)+eage[i].w;
 96         ans=min(d,ans);
 97     }
 98     printf("%d
",ans>10000000?-1:ans);
 99     return ;
100 }
101 int main()
102 {
103     scanf("%d",&T);
104     while(T--)
105     {
106         scanf("%d%d",&n,&m);
107         while(m--)
108         {
109             scanf("%d%d%d",&x,&y,&z);
110             add(x,y,z);add(y,x,z);
111         }
112         Astar();
113         clear();
114     }
115 }
View Code

T3:又是一道足以把评测机卡崩的题   好题标记

毫无头绪?

是的 毫无头绪是真的毫无头绪,但根据数据范围可以推测复杂度应该接近O(n)(T=50,5000ms限制啧啧啧)

就此展开,感觉dp否了,递推。。。也不大可能,所以我们自然会想到搜索图论,(好吧我承认有点牵强,但说实话,O(n)的算法真的不多,这总不能单队维护吧QwQ)

然后就会有奇妙的发现:如果两两连边HiaHiaHia,没神魔卵用,就会发现其实操作只是把边反过来而已。而题目的目标就是每个点的入度<=1。

既然这样,那就继续探索。把这个图画出来,手玩小样例后发现图被分成了几个连通块,然后就可以分类讨论了:

1.m>n 显然是不对的,因为要保证每条边都指向一个点,每个点又要被不大于一条边指(这这这不可能)。

2.m=n 是棵基环树!!!!先考虑环,环可以顺时针也可以逆时针,所以分两种情况,再看以环为根的子树,不管环咋转,子树永远是外向的,所以一遍dfs即可

3.m<n 要保证图联通所以这一定是棵树,而且是棵外向树,快乐吗?可问题是根是不确定的。

下面引入新概念:

二次扫描和换根法:对于不定根的树状DP,暴力枚举是O(n^2),但我们可以先考虑一个点,再考虑用已知点更新未知点,即考虑换根的代价。

但只适用于不管换不换根,旧根与新根的共同子树状态不变。(纯属博猪个人理解,如有错误,多谢指正)

好啦,有了这个强大的方法,我们就可以成功AC了

AC 代码:

  1 #include<bits/stdc++.h>
  2 #define mem(a) memset(a,0,sizeof(a))
  3 #define MAXN 200000
  4 using namespace std;
  5 const int mod=998244353;
  6 struct node{
  7     int head[MAXN],to[MAXN],nxt[MAXN],cnt,dfn[MAXN],now_minn,now_tot,v[MAXN];long long tot;
  8     int use,sum,fa[MAXN],d[MAXN],f[MAXN];
  9     long long ans,minn;
 10     bool orz[MAXN],vst[MAXN],inlop[MAXN],vst1[MAXN],vst2[MAXN];
 11     vector<int>lop;
 12     void clear()
 13     {
 14         lop.clear();cnt=1;ans=1;minn=0;tot=0;mem(fa);mem(v);
 15         mem(inlop);mem(vst);mem(vst1);mem(vst2);
 16         mem(d);mem(f);
 17         mem(dfn);
 18         mem(head);
 19     }
 20     void add(int u,int v,int opt)
 21     {
 22         to[++cnt]=v;
 23         nxt[cnt]=head[u];
 24         head[u]=cnt;
 25         orz[cnt]=opt;
 26         return ;
 27     }
 28     inline int Rd()
 29     {
 30         int x=0;
 31         char c=getchar();
 32         int f=1;
 33         while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
 34         while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
 35         return x*f;
 36     }
 37     bool Getlop(int x,int edge)
 38     {
 39         dfn[x]=++tot;
 40         for(int i=head[x];i;i=nxt[i])
 41         {
 42             if(i==(edge^1))continue;
 43             int y=to[i];
 44             if(dfn[y])
 45             {
 46                 if(sum==1){sum++;continue;}
 47                 if(sum>1)return 0;
 48                 sum++;
 49                 inlop[y]=1;
 50                 lop.push_back(y);
 51                 if(orz[i])use=1;
 52                 else use=0;
 53                 int p=x;
 54                 while(p!=y)
 55                 {
 56                     lop.push_back(p);
 57                     inlop[p]=1;
 58                     use+=v[p];
 59                     p=fa[p];
 60                 }
 61             }
 62             else {fa[y]=x,v[y]=orz[i];if(Getlop(y,i)==0)return 0;}
 63         }
 64         return 1;
 65     }
 66     void dp(int x)
 67     {
 68         vst[x]=1;
 69         for(int i=head[x];i;i=nxt[i])
 70         {
 71             int y=to[i];
 72             if(vst[y]||inlop[y])continue;
 73             dp(y);
 74             d[x]+=d[y];
 75             if(!orz[i])d[x]++;
 76         }
 77         return ;
 78     }
 79     void dprt(int x)
 80     {
 81         vst2[x]=1;
 82         for(int i=head[x];i;i=nxt[i])
 83         {
 84             int y=to[i];
 85             if(vst2[y]||inlop[y])continue;
 86             if(orz[i])f[y]=f[x]+1;
 87             else f[y]=f[x]-1;
 88             dprt(y);
 89         }
 90         return ;
 91     }
 92     void Getans(int x)
 93     {
 94         vst1[x]=1;
 95         if(f[x]==now_minn){now_tot++;}
 96         else if(f[x]<now_minn){now_tot=1;now_minn=f[x];}
 97         for(int i=head[x];i;i=nxt[i])
 98         {
 99             int y=to[i];
100             if(vst1[y]||inlop[y])continue;
101             Getans(y);
102         }
103         return ;
104     }
105     void work()
106     {
107         clear();
108     //    cout<<tot<<endl;
109         int n=Rd();
110         for(int i=1;i<=n;i++)
111         {
112             int a=Rd(),b=Rd();
113             add(b,a,1);
114             add(a,b,0);
115         }
116         for(int i=1;i<=2*n;i++)
117         {
118             if(dfn[i]||!head[i])continue;
119             lop.clear();sum=0;
120             use=0;
121             bool ok=Getlop(i,0);
122             if(!ok){puts("-1 -1");return ;}
123             else 
124             {
125                 if(!lop.size())
126                 {
127                     now_minn=0x7f7f7f7f;
128                     now_tot=0;dp(i);
129                     f[i]=d[i];
130                     dprt(i);
131                     Getans(i);
132                     minn+=now_minn;
133                     ans=ans*now_tot%mod;
134                 }
135                 else 
136                 {
137                     int len=lop.size();
138                     //cout<<len<<" "<<use<<endl;
139                     if(use+use^len)
140                     {
141                         minn+=min(use,len-use);
142                         for(int i=0;i<len;i++)
143                         {
144                             now_minn=0x7f7f7f7f;
145                             now_tot=0;dp(lop[i]);
146                             now_minn=d[lop[i]];
147                             minn+=now_minn;
148                         }
149                     }
150                     else 
151                     {
152                         minn+=use;
153                         ans=ans*2%mod;
154                         for(int i=0;i<len;i++)
155                         {
156                             now_minn=0x7f7f7f7f;
157                             now_tot=0;dp(lop[i]);
158                             now_minn=d[lop[i]];
159                             minn+=now_minn;
160                         }
161                     }
162                 }
163             }
164         }
165         printf("%lld %lld
",minn,ans);
166     }
167 }E;
168 int main()
169 {
170 //    freopen("out.in","r",stdin);
171 //    freopen("b.txt","w",stdout);
172     int t=E.Rd();
173     while(t--)E.work();
174     return 0;
175 }
View Code

总结:

1.考试审题!囧

2.仔细想想自己的复杂度能不能下降,能不能避免不必要的枚举,哪怕搜索打个剪枝也是好的。

3.暴力别打错了!

4.结合着期望的复杂度去思考问题,相信自己。

下次加油!

 

原文地址:https://www.cnblogs.com/hzoi-kx/p/11219318.html