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 */
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]<<' '; 95 getans(n+1); 96 cout<<' '; 97 return 0; 98 }
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 }