JSOI2018R2题解

D1T1:潜入行动

裸的树上DP。f[i][j][0/1][0/1]表示以i为根的子树放j个设备,根有没有放,根有没有被子树监听,的方案数。转移显然。

 1 #include<cstdio>
 2 #include<algorithm>
 3 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
 4 typedef long long ll;
 5 using namespace std;
 6 
 7 const int N=100010,mod=1e9+7;
 8 ll tmp[110][2][2];
 9 int n,k,cnt,u,v,sz[N],dp[N][110][2][2],h[N],to[N<<1],nxt[N<<1];
10 
11 void ins(int u,int v){ to[++cnt]=v; nxt[cnt]=h[u]; h[u]=cnt; }
12 void add(int &x,ll y){ if (x+y>=mod) x=x+y-mod; else x=x+y; }
13 
14 void dfs(int x,int fa){
15     sz[x]=1; dp[x][1][1][0]=1; dp[x][0][0][0]=1; 
16     for (int i=h[x]; i; i=nxt[i]){
17         int v=to[i];
18         if (v==fa) continue;
19         dfs(v,x);
20         int tx=min(k,sz[x]),tv=min(k,sz[v]);
21         rep(j,0,tx) rep(a,0,1) rep(b,0,1)
22             tmp[j][a][b]=dp[x][j][a][b],dp[x][j][a][b]=0;
23         rep(j,0,tx){
24             for(int a=0; a<=tv && j+a<=k; a++){
25                 add(dp[x][j+a][0][0],tmp[j][0][0]*dp[v][a][0][1]%mod);
26                 add(dp[x][j+a][0][1],(tmp[j][0][0]*dp[v][a][1][1]+tmp[j][0][1]*(dp[v][a][0][1]+dp[v][a][1][1]))%mod);
27                 add(dp[x][j+a][1][0],tmp[j][1][0]*(dp[v][a][0][0]+dp[v][a][0][1])%mod);
28                 add(dp[x][j+a][1][1],(tmp[j][1][0]*(dp[v][a][1][0]+dp[v][a][1][1])+tmp[j][1][1]*(dp[v][a][0][0]+dp[v][a][1][0])+tmp[j][1][1]*(dp[v][a][0][1]+dp[v][a][1][1]))%mod);
29             }
30         }
31         sz[x]+=sz[v];
32     }
33 }
34 
35 int main(){
36     freopen("action.in","r",stdin);
37     freopen("action.out","w",stdout);
38     scanf("%d%d",&n,&k);
39     rep(i,2,n) scanf("%d%d",&u,&v),ins(u,v),ins(v,u);
40     dfs(1,-1);
41     printf("%d\n",(dp[1][k][1][1]+dp[1][k][0][1])%mod);
42     return 0;
43 }
D1T1

 

D2T2:防御网络

https://blog.csdn.net/scar_lyw/article/details/80410420

首先连接环的桥边的贡献很好算,tarjan过程中可直接求出。然后对于每一个环,求出环上被选中边数的期望。
一个环被选中的边,一定是环长减去最大的一个空隙。我们枚举环上的[s,s+len]中的点被选中(一个点被选中当且仅当子图S中包含这个点或这个点连出的外向树中的点)。设DP状态dp[s][len][k]表示[s,s+len]中有点且这段的最大空隙为k的方案数。求出dp数组后贡献就很容易求得了,注意还有一个空隙是环长-len。

 1 #include<cstdio>
 2 #include<algorithm>
 3 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
 4 #define For(i,x) for (int i=h[x],k; i; i=nxt[i])
 5 using namespace std;
 6 
 7 const int N=1010,mod=1e9+7;
 8 int n,m,u,v,cnt,ans,f[N],p2[N],sz[N],vis[N],fa[N];
 9 int dp[N][2],sm[N][2],h[N],to[N],nxt[N],dep[N];
10 void add(int u,int v){ to[++cnt]=v; nxt[cnt]=h[u]; h[u]=cnt; }
11 
12 int ksm(int a,int b){
13     int res=1;
14     for (; b; a=1ll*a*a%mod,b>>=1)
15         if (b & 1) res=1ll*res*a%mod;
16     return res;
17 }
18 
19 void calc(int s,int len,int n){
20     rep(i,s-1,n) dp[i][0]=dp[i][1]=sm[i][0]=sm[i][1]=0;
21     dp[s][0]=sm[s][0]=f[s];
22     rep(i,s+1,n){
23         int l=max(s,i-len+1)-1;
24         if (i-len>=s) dp[i][1]=(dp[i][1]+1ll*(dp[i-len][0]+dp[i-len][1])*f[i])%mod;
25         dp[i][1]=(dp[i][1]+1ll*f[i]*(sm[i-1][1]-sm[l][1]+mod))%mod;
26         dp[i][0]=(dp[i][0]+1ll*f[i]*(sm[i-1][0]-sm[l][0]+mod))%mod;
27         sm[i][0]=(sm[i-1][0]+dp[i][0])%mod; sm[i][1]=(sm[i-1][1]+dp[i][1])%mod;
28         ans=(ans+1ll*min(i-s,n-len)*dp[i][1])%mod;
29     }
30 }
31 
32 void solve(int u,int v){
33     int cur=v,lst=0,tot=0;
34     for (; cur!=u; lst=cur,cur=fa[cur])
35         f[++tot]=p2[sz[cur]-sz[lst]],vis[cur]=1;
36     f[++tot]=p2[n-sz[lst]];
37     rep(i,1,tot-1) rep(j,1,tot-i) calc(i,j,tot);
38 }
39 
40 void dfs(int x){
41     dep[x]=dep[fa[x]]+1; sz[x]=1;
42     For(i,x) if (!dep[k=to[i]]) fa[k]=x,dfs(k),sz[x]+=sz[k];
43         else if (dep[k]>dep[x]) solve(x,k);
44 }
45 
46 int main(){
47     freopen("defense.in","r",stdin);
48     freopen("defense.out","w",stdout);
49     scanf("%d%d",&n,&m);
50     rep(i,1,m) scanf("%d%d",&u,&v),add(u,v),add(v,u);
51     p2[0]=1; rep(i,1,n) p2[i]=p2[i-1]*2ll%mod;
52     rep(i,1,n) p2[i]--;
53     dfs(1);
54     rep(i,2,n) if (!vis[i]) ans=(ans+1ll*p2[sz[i]]*p2[n-sz[i]])%mod;
55     ans=1ll*ans*ksm(p2[n]+1,mod-2)%mod;
56     printf("%d\n",ans);
57     return 0;
58 }
D2T2

 

D1T3:绝地反击

如果确定这个正n边形的话,二分+Dinic就好了。退火/爬山旋转正n边形即可。

正解是网络流退流,未看。

 

D2T1:部落战争

https://www.cnblogs.com/HocRiser/p/10268249.html

 

D2T2:扫地机器人

留坑。

 

D2T3:军训列队

显然集合后每个人的相对位置可以不改变,那么把所求的式子列出来就会发现是一些定值加上关于每个人的坐标和集合点的差的绝对值的数。拆成两部分,显然左半部分向右走,右半部分向左走。

在主席树上二分这个分界点,维护区间中点的坐标和,剩下的直接求解。

 1 #include<cstdio>
 2 #include<algorithm>
 3 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
 4 #define lson ls[x],L,mid
 5 #define rson rs[x],mid+1,R
 6 typedef long long ll;
 7 using namespace std;
 8 
 9 const int N=500010,M=12500010;
10 ll c,sum[N],sm[M];
11 int n,Q,m,l,r,k,K,nd,a[N],rt[N],v[M],ls[M],rs[M];
12 
13 void ins(int y,int &x,int L,int R,int k){
14     x=++nd; ls[x]=ls[y]; rs[x]=rs[y]; v[x]=v[y]+1; sm[x]=sm[y]+k;
15     if (L==R) return;
16     int mid=(L+R)>>1;
17     if (k<=mid) ins(ls[y],lson,k); else ins(rs[y],rson,k);
18 }
19 
20 int que(int x,int y,int L,int R,int k){
21     if (L==R){ c+=sm[x]-sm[y]; return v[x]-v[y]; }
22     int mid=(L+R)>>1,t=v[ls[x]]-v[ls[y]];
23     if (k+t-1<mid) return que(ls[x],ls[y],L,mid,k);
24     else{
25         c+=sm[ls[x]]-sm[ls[y]];
26         return v[ls[x]]-v[ls[y]]+que(rs[x],rs[y],mid+1,R,k+t);
27     }
28 }
29 
30 int main(){
31     freopen("line.in","r",stdin);
32     freopen("line.out","w",stdout);
33     scanf("%d%d",&n,&Q);
34     rep(i,1,n) scanf("%d",&a[i]),m=max(m,a[i]),sum[i]=sum[i-1]+a[i];
35     rep(i,1,n) ins(rt[i-1],rt[i],1,m,a[i]);
36     while (Q--){
37         scanf("%d%d%d",&l,&r,&k); c=0;
38         int tot=que(rt[r],rt[l-1],1,m,k),len=r-l+1;
39         ll s1=1ll*tot*k+1ll*tot*(tot-1)/2-c;
40         ll s2=sum[r]-sum[l-1]-c-1ll*(len-tot)*k-1ll*(tot+len-1)*(len-tot)/2;
41         printf("%lld\n",s1+s2);
42     }
43     return 0;
44 }
D2T3

 

posted @ 2019-01-23 11:46  HocRiser  阅读(321)  评论(0编辑  收藏  举报