1.

给你一个圆环,上面均匀分布着2N 个点,顺时针依次标为1~2N。
现在把这些点分成N 对,然后把每一对点之间连一条线段。两个点被认为是互相可达的当且仅当可以从一个点开始,通过在线段上行走走到另一个点。连通块数指为将每一对互相可达的点之间连一条边形成一张图,这张图中的连通分量数量。
现在有一些点对是确定的,请你回答所有可能的划分点对的方案中,连通块数的和是多少。N<=300

  1 #pragma GCC optimize 2
  2 #define mod 1000000007
  3 #define G2 500000004
  4 #include<bits/stdc++.h>
  5 using namespace std;
  6 typedef long long int ll;
  7 int n,m;
  8 int a[5555][2];
  9 ll f[5555],C[1555][1555],fac[5555];
 10 inline void add(ll&x,ll y)
 11 {
 12     x=(x+y)%mod;
 13 }
 14 inline void init()
 15 {
 16     f[0]=1;
 17     for(int i=1;i<=1000;++i)
 18         f[i]=f[i-1]*(2*i-1)%mod;
 19     fac[0]=1;
 20     for(int i=1;i<=1000;++i)
 21         fac[i]=fac[i-1]*i%mod;
 22     for(int i=0;i<=1000;++i)
 23     {
 24         C[i][0]=1;
 25         for(int j=1;j<=i;++j)
 26             C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
 27     }
 28 }
 29 int num[666],sumL[666],sumR[666];
 30 ll g[666],h[666];
 31 int cantL[666],cantR[666];
 32 bool to[666];
 33 inline ll get(int l,int r)
 34 {
 35     int s0=l-1,s1=2*n-r,s2=r-l-1;
 36     for(int i=1;i<=m;++i)
 37         if(a[i][0]==l&&a[i][1]!=r)
 38             return 0;
 39         else if(a[i][1]==r&&a[i][0]!=l)
 40             return 0;
 41         else if(a[i][0]==r||a[i][1]==l)
 42             return 0;
 43     int TI=0;
 44     for(int i=l-1;i>=1;--i)
 45         num[i]=++TI;
 46     for(int i=2*n;i>=r+1;--i)
 47         num[i]=++TI;
 48     for(int i=r-1;i>=l+1;--i)
 49         num[i]=++TI;
 50     ll s=0;
 51     memset(cantL,0,sizeof(cantL));
 52     memset(cantR,0,sizeof(cantR));
 53     memset(to,0,sizeof(to));
 54     memset(g,0,sizeof(g));
 55     memset(h,0,sizeof(h));
 56     for(int i=1;i<=m;++i)
 57     {
 58         if(a[i][0]==l&&a[i][1]==r)
 59             continue;
 60         to[num[a[i][0]]]=1;
 61         to[num[a[i][1]]]=1;
 62         int L=min(num[a[i][1]],num[a[i][0]]);
 63         int R=max(num[a[i][1]],num[a[i][0]]);
 64         ++cantL[L],--cantL[R];
 65         --cantR[L],++cantR[R];
 66     }
 67     for(int i=1;i<=s0+s1+s2;++i)
 68         sumL[i]=sumL[i-1]+(to[i]==0),cantL[i]+=cantL[i-1];
 69     cantR[s0+s1+s2+1]=0;
 70     for(int i=s0+s1+s2;i>=1;--i)
 71         cantR[i]+=cantR[i+1];
 72     sumR[s0+s1+s2+1]=0;
 73     for(int i=s0+s1+s2;i>=1;--i)
 74         sumR[i]=sumR[i+1]+(to[i]==0);
 75     if(sumL[s0]%2==0)
 76         g[s0]=f[sumL[s0]>>1];
 77     for(int i=s0+1;i<=s0+s1;++i)
 78     {
 79         if(cantL[i]||sumL[i]%2==1)
 80             continue;
 81         g[i]=f[sumL[i]>>1];
 82         for(int j=s0;j<i;++j)
 83         {
 84             if(cantL[j])
 85                 continue;
 86             if((sumL[i]-sumL[j])%2==0)
 87                 g[i]=(g[i]-g[j]*f[(sumL[i]-sumL[j])>>1]%mod+mod)%mod;
 88         }
 89     }
 90     if(sumR[s0+s1+1]%2==0)
 91         h[s0+s1+1]=f[sumR[s0+s1+1]>>1];
 92     for(int i=s0+s1;i>=s0;--i)
 93     {
 94         if(cantR[i]||sumR[i]%2==1)
 95             continue;
 96         h[i]=f[sumR[i]>>1];
 97         for(int j=i+1;j<=s0+s1+1;++j)
 98         {
 99             if(cantR[j])
100                 continue;
101             if((sumR[i]-sumR[j])%2==0)
102                 h[i]=(h[i]-h[j]*f[(sumR[i]-sumR[j])>>1]%mod+mod)%mod;
103         }
104     }
105     for(int i=s0;i<=s0+s1;++i)
106         for(int j=i+1;j<=s0+s1+1;++j)
107         {
108             if(cantL[i]||cantR[j]||(sumL[j-1]-sumL[i])%2==1)
109                 continue;
110             else if(g[i]&&h[j])
111                 add(s,g[i]*h[j]%mod*f[(sumL[j-1]-sumL[i])>>1]%mod);
112         }
113     return s;
114 }
115 inline void solve()
116 {
117     cin>>n>>m;
118     for(int i=1;i<=m;++i)
119     {
120         cin>>a[i][0]>>a[i][1];
121         if(a[i][0]>a[i][1])
122             swap(a[i][0],a[i][1]);
123     }
124     ll ans=0;
125     for(int i=1;i<=2*n;++i)
126         for(int j=i+1;j<=2*n;++j)
127             ans=(ans+get(i,j))%mod;
128     cout<<ans<<endl;
129 }
130 int main()
131 {
132     freopen("count.in","r",stdin);
133     freopen("count.out","w",stdout);
134     ios::sync_with_stdio(false);
135     init();
136     int T;
137     cin>>T;
138     while(T--)
139         solve();
140     return 0;
141 }
142 /*
143 2
144 2 0
145 20 10
146 10 18
147 11 17
148 14 7
149 4 6
150 30 28
151 19 24
152 29 22
153 25 32
154 38 34
155 36 39
156 */
View Code

 

2.

给你一张有重边无自环的图,共有n 个点,m 条边。每一条边正着走和反着走各有一个要花费的时间。
请你求出在不走重边的情况下,从1 号点出发再回到1 号点至少需要多久。

 1 #include<bits/stdc++.h>
 2 #define ll long long
 3 #define lld long double
 4 using namespace std;
 5 template<typename tn> void read(tn &a){
 6     tn x=0,f=1; char c=' ';
 7     for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
 8     for(;isdigit(c);c=getchar()) x=x*10+c-'0';
 9     a=x*f;
10 }
11 const int N = 21000;
12 int n,m;
13 struct edge{int v,w,num;};
14 class Graph{
15 public:
16     int n;
17     vector<edge> e[N];
18     int d[N],fr[N],pre[N];
19     priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > q;
20     void init(int num){
21         n=num;
22     }
23     void add(int x,int y,int z,int num=0){
24         e[x].push_back({y,z,num});
25     }
26     void dij(){
27         memset(d,63,sizeof(d));d[1]=0;
28         q.push(make_pair(0,1));
29         while(!q.empty()){
30             int u=q.top().second,dist=q.top().first;q.pop();
31             if(dist!=d[u]) continue;
32             for(auto v:e[u])
33                 if(d[v.v]>d[u]+v.w){
34                     fr[v.v]=u==1?v.num:fr[u];
35                     pre[v.v]=u;
36                     d[v.v]=d[u]+v.w;
37                     q.push(make_pair(d[v.v],v.v));
38                 }
39         }
40     }
41 }G1,G2;
42 void getans(int x,int flag=0){
43     if(x==1){cout<<1<<' ';return;}
44     if(flag){
45         getans(G1.pre[x],1);
46         cout<<x<<' ';
47         return;
48     }
49     if(G2.pre[x]>1){
50         getans(G2.pre[x]);cout<<(x>n?1:x)<<' ';
51     }
52     else{
53         for(int i=2;i<=n;i++)
54             for(auto v:G1.e[i])
55                 if(x>n){
56                     if(v.v==1&&G1.fr[i]!=v.num&&G1.d[i]+v.w==G2.d[x]){
57                         getans(i,1);
58                         cout<<1<<' ';
59                         return;
60                     }
61                 }
62                 else{
63                     if(v.v==x&&G1.fr[i]!=G1.fr[v.v]&&G1.d[i]+v.w==G2.d[x]){
64                         getans(i,1);
65                         cout<<x<<' ';
66                         return;
67                     }
68                 }
69     }
70 }
71 int main(){
72     freopen("circuit.in","r",stdin);
73     freopen("circuit.out","w",stdout);
74     read(n);read(m);
75     G1.init(n);
76     for(int i=1;i<=m;i++){
77         int x,y,z1,z2;read(x);read(y);read(z1);read(z2);
78         G1.add(x,y,z1,i);
79         G1.add(y,x,z2,i);
80     }
81     G1.dij();
82     G2.init(n+1);
83     for(int i=2;i<=n;i++)
84         for(auto v:G1.e[i])
85             if(v.v==1){
86                 if(G1.fr[i]==v.num) G2.add(i,n+1,v.w);
87                 else G2.add(1,n+1,G1.d[i]+v.w);
88             }
89             else{
90                 if(G1.fr[i]==G1.fr[v.v]) G2.add(i,v.v,v.w);
91                 else G2.add(1,v.v,G1.d[i]+v.w);
92             }
93     G2.dij();
94     cout<<G2.d[n+1]<<'\n';
95     getans(n+1);
96     cout<<'\n';
97     return 0;
98 }
View Code

3.有n 只怪物,每只有一个生命值ai, 即要打ai 下才会死。所有的怪物外貌一样,无法分辨。
你想要狩猎至少m 只怪物,并且你知道怪物的血量(但不能和怪物一一对应),求出最坏情况下最少的攻击次数。N<=1000

对于这种奇怪的题目,一定要找到一些特殊的性质。

我们先考虑m=1怎么做。那么我们会挑选一个选定的量x(当然是怪物的一个存在的血量),每次挑出一个怪物。若在x步内能解决问题,那么久停止。否则立刻换另一个怪物。

可以发现,最优的策略一定在这里出现。因为如果存在一种策略,会在过程中改变这个选定的量x,那么一开始就应该改变。

那么将a数组从大到小排序,答案为min{ai*i}。为什么要乘i?应为每次都可能选中一个大于x的怪物。

现在m不为1。仍然是将a数组从大到小排序,我们现在选出一个大小为m的下标集合(下标从小到大拍好),表示我以此选定的量x。那么这种策略的答案为$a_{s_1}*{s_1}+\sum_{i=2}^{m}{a_{s_i}*(s_i-s_{i-1})}$,理由和m=1是一样的。

那么写出dp方程后,即可斜率优化。复杂度O(n^2)。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long double ld;
 4 const int maxn=2E3+5;
 5 const int inf=INT_MAX;
 6 int n,m,a[maxn];
 7 int f[maxn][maxn];
 8 int q[maxn][maxn],qL[maxn],qR[maxn];
 9 inline ld slope(int i1,int i2,int j)
10 {
11     return (ld)(f[i1][j]-f[i2][j])/(i1-i2);
12 }
13 inline int get(int i,int j,int k)
14 {
15     return f[k][j-1]+(i-k)*a[i];
16 }
17 inline void solve()
18 {
19     cin>>n>>m;
20     for(int i=1;i<=n;++i)
21         cin>>a[i];
22     sort(a+1,a+n+1);
23     reverse(a+1,a+n+1);
24     for(int i=0;i<=n;++i)
25         for(int j=0;j<=n;++j)
26             f[i][j]=inf;
27     f[0][0]=0;
28     memset(q,0,sizeof(q));
29     memset(qL,0,sizeof(qL));
30     memset(qR,0,sizeof(qR));
31     for(int i=0;i<=n;++i)
32         qL[i]=1,qR[i]=0;
33     qR[0]=1;
34     q[0][1]=0;
35     int ans=inf;
36     for(int i=1;i<=n;++i)
37         for(int j=i;j>=1;--j)
38         {
39             if(j==1)
40                 f[i][j]=get(i,j-1,0);
41             else
42             {
43                 while(qL[j-1]<qR[j-1]&&slope(q[j-1][qR[j-1]-1],q[j-1][qR[j-1]],j-1)>=a[i])
44                     --qR[j-1];
45                 f[i][j]=get(i,j,q[j-1][qR[j-1]]);
46             }
47             while(qL[j]<qR[j]&&slope(q[j][qR[j]-1],q[j][qR[j]],j)>=slope(q[j][qR[j]],i,j))
48                 --qR[j];
49             q[j][++qR[j]]=i;
50             ans=min(ans,f[i][m]);
51         }
52 //    for(int i=1;i<=n;++i,cout<<endl)
53 //        for(int j=1;j<=i;++j)
54 //            cout<<f[i][j]<<" ";
55     cout<<ans<<endl;
56 }
57 int main()
58 {
59 //    freopen("hunt.in","r",stdin);
60 //    freopen("hunt.out","w",stdout);
61     ios::sync_with_stdio(false);
62     int T;
63     cin>>T;
64     while(T--)
65         solve();
66     return 0;
67 }
View Code

 

 posted on 2020-06-10 14:37  GreenDuck  阅读(219)  评论(0编辑  收藏  举报