版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_40032278/article/details/82145395
div.1
T1
题意
给一个操作序列,每次向当前方向走a[i]步,然后90度转a[i]次,问做T次该操作序列之后的位置与起始位置的manhattan距离。
思路
首先肯定做完4次操作序列后肯定能还原方向,那么模拟做完剩下的就行了。
//tc is healthy, just do it
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
class RobotHerb {
public:
long long getdist( int T, vector <int> a );
};
long long RobotHerb::getdist(int T, vector <int> a) {
long long x=0,y=0,d=0,ans;
int i,j;
for(i=0;i<4;i++)
for(j=0;j<a.size();j++)
{
if(d==0) y+=a[j];
else if(d==1) x+=a[j];
else if(d==2) y=y-a[j];
else if(d==3) x=x-a[j];
d=d+a[j];
d=d%4;
}
ans=(abs(x)+abs(y))*(T/4);
x=0;
y=0;
d=0;
for(i=0;i<T%4;i++)
for(j=0;j<a.size();j++)
{
if(d==0) y+=a[j];
else if(d==1) x+=a[j];
else if(d==2) y=y-a[j];
else if(d==3) x=x-a[j];
d=d+a[j];
d=d%4;
}
return ans+abs(x)+abs(y);
}
T2
题意
N个结点的树每个结点会随机被分给某一方,考虑其中一方的结点,你需要加一些线使得它们形成一个新的联通块,当某个结点加入第二条边的时候会花费1的代价,问期望的最小代价。
思路
首先两方是对称的,所以只用算出一方的代价然后乘2就行了。接下来假设当前方有n个结点,m个联通块,那么当前方的代价为max(2*m-n-2,0),这个感觉挺显然的。所以我们可以dp当前有几个结点几个联通块,但我们发现后面两个信息可以整合,然后每个结点再做一次背包就完了。
定义d[2][i][j][k]为第i 个节点,有j个联通块,有k个点在里面的方案数.
#include <bits/stdc++.h>
using namespace std;
vector<int>e[100];
long long p[100],d[2][100][100][100];
int n;
class CentaurCompany {
public:
double getvalue(vector <int> a, vector <int> b);
};
void dfs(int u,int f)
{
int q,i,j,k,l,v;
for(q=0;q<e[u].size();q++)
{
v=e[u][q];
if(v==f)continue;
dfs(v,u);
for(i=n;i>=0;i--)
for(j=n;j>=0;j--)
for(k=n;k>=0;k--)
for(l=n;l>=1;l--)
{
d[0][u][i+k][j+l]+=d[0][u][i][j]*(d[0][v][k][l]+d[1][v][k][l]);
d[1][u][i+k][j+l]+=d[1][u][i][j]*d[0][v][k][l];
d[1][u][i+k-1][j+l]+=d[1][u][i][j]*d[1][v][k][l];
}
}
d[0][u][0][0]=0;
}
double CentaurCompany::getvalue(vector <int> a, vector <int> b) {
int i,x,y,j,k;
long long ans=0,c;
p[0]=1;
for(i=1;i<50;i++)p[i]=2*p[i-1];
n=a.size()+1;
for(i=1;i<=n;i++)e[i].clear();
for(i=0;i<n-1;i++)
{
x=a[i];
y=b[i];
e[x].push_back(y);
e[y].push_back(x);
}
memset(d,0,sizeof(d));
for(j=1;j<=n;j++)
d[0][j][0][0]=d[1][j][1][1]=1;
dfs(1,-1);
for(j=0;j<=n;j++)
for(k=0;k<=n;k++)
{
x=2*(j-1)-k;
c=1ll*d[0][1][j][k]*x;
ans+=max(0ll,c);
c=d[1][1][j][k]*x;
ans+=max(0ll,c);
}
return ans*1.0/p[n-1];
}
div.2
T3
题意
给你一棵树,求在割去任意条边后,剩下的联通块的个数总和(全空也算是一个),也就是指那些使集内的点能只通过集内的点到达任意一个集内的点的点集.(点数<=50)
思路
我们把这个问题剖分成对于一棵树,在包含它的根时,有多少个这样的点集.那么在不包含它的情况可以递归下去求解.
那么只需要递归地求出儿子的解,然后在父亲上跑一个背包就好了.
//tc is healthy, just do it
#include <bits/stdc++.h>
using namespace std;
vector<int> e[10010];
long long ans=0,d[101][101];
class CentaurCompanyDiv2 {
public:
long long count( vector <int> a, vector <int> b );
};
void dfs(int u,int f)
{
int i,v,j,k;
for(i=0;i<e[u].size();i++)
{
v=e[u][i];
if(v==f) continue;
dfs(v,u);
for(k=55;k>=1;k--)
for(j=56-k-1;j>=0;j--)
if(j+k<56) d[u][j+k]+=d[u][k]*d[v][j];
}
}
long long CentaurCompanyDiv2::count(vector <int> a, vector <int> b) {
int n=a.size()+1,i,j,x,y;
for(i=1;i<=n;i++)e[i].clear();
for(i=0;i<n-1;i++)
{
x=a[i];
y=b[i];
e[x].push_back(y);
e[y].push_back(x);
}
for(i=1;i<=n;i++){
for(j=0;j<56;j++)d[i][j]=0;
d[i][1]=1;
}
dfs(1,-1);
ans=0;
for(i=1;i<=n;i++)
for(j=0;j<56;j++)
ans+=d[i][j];
return ans+1;
}