比赛时间:2018.10.16 选手:lrllrl 得分:70+0+10=80 用时:2小时
(让我想起了NOIPDAY1T1)
设
d
p
[
i
]
dp[i]
dp[i] 表示走到
i
i
i 位置采到的最大数量,然后直接转移就好了。唯一的问题是
i
i
i 的范围可以达到
1
e
9
1e9
1e9。不过我们知道,由于
4
4
4 和
7
7
7 互质,不能用形如
4
x
+
7
y
4x+7y
4x+7y 表示的最大整数为
4
×
7
−
4
−
7
=
17
4\times 7-4-7=17
4×7−4−7=17,所以我们把所有差值大于
17
17
17 的看成
18
18
18 就行了。
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=1e5+10;
int n,m,dp[N*20],a[N],ans;
namespace pts60{
void solve()
{
for(int i=1;i<=n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
a[y]=x;
}
for(int i=0;i<=m;i++)
dp[i+4]=max(dp[i+4],dp[i]+a[i+4]),dp[i+7]=max(dp[i+7],dp[i]+a[i+7]),ans=max(ans,dp[i]);
printf("%d\n",ans);
}
}
struct node{
int a,b;
bool operator <(const node&rhs)const{
return b<rhs.b;}
}p[N*20];
int b[N*20];
int main()
{
//freopen("mining.in","r",stdin);
//freopen("mining.out","w",stdout);
memset(dp,-1,sizeof(dp));dp[0]=0;
scanf("%d%d",&n,&m);
//if(m<=100000)pts60::solve();
for(int i=1;i<=n;i++)
scanf("%d%d",&p[i].a,&p[i].b);
sort(p+1,p+n+1);
int maxn=0;
for(int i=1;i<=n;i++)
maxn+=(p[i].b-p[i-1].b<=17)?p[i].b-p[i-1].b:18,b[maxn]+=p[i].a;
for(int i=0;i<=maxn;i++)
if(dp[i]>=0)dp[i+4]=max(dp[i+4],dp[i]+b[i+4]),dp[i+7]=max(dp[i+7],dp[i]+b[i+7]),ans=max(ans,dp[i]);
printf("%d\n",ans);
return 0;
}
#include<cstdio>
#include<algorithm>
using namespace std;
const int INF=0x3f3f3f3f;
int ans,t,n,m,a[35][35],f[1805][35][35],N;
//f[s][x][y]表示到(x,y)和为s的最小∑ai^2
int main()
{
//freopen("in.txt","r",stdin);
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);ans=INF;N=(n+m)*30;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&a[i][j]);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
for(int k=0;k<=N;k++)
f[k][i][j]=1000000;
f[a[1][1]][1][1]=a[1][1]*a[1][1];
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
for(int k=0;k<=N;k++)
if(f[k][i][j]!=1000000)
{
int x=i+1,y=j,v=a[x][y];
f[k+v][x][y]=min(f[k+v][x][y],f[k][i][j]+v*v);
x=i,y=j+1,v=a[x][y];
f[k+v][x][y]=min(f[k+v][x][y],f[k][i][j]+v*v);
}
for(int i=1;i<=N;i++)
if(f[i][n][m]!=1000000)
ans=min(ans,(n+m-1)*f[i][n][m]-i*i);
printf("%d\n",ans);
}
return 0;
}
#include<cstdio>
const int N=1e5+1000,M=16;
int n,m,hd[N],tot,f[N][M],F[N],g[M];
//f[i][j]表示走到i节点时j(0~15)的个数
//F[i]表示i点出去的所有路径中不会被异或掉的部分
struct Edge{
int v,w,nx;
}e[N<<1];
void add(int u,int v,int w)
{
e[++tot].v=v;
e[tot].w=w;
e[tot].nx=hd[u];
hd[u]=tot;
}
void dfs1(int u,int fa)//求v的子树的边权和
{
for(int i=hd[u];i;i=e[i].nx)
{
int v=e[i].v;
if(v==fa)continue;
dfs1(v,u);
F[u]+=F[v]+e[i].w/M;//大于15的部分额外记一个值
f[u][e[i].w%M]++;
for(int j=0;j<M;j++)
{
int k=j+e[i].w;
F[u]+=k/M*f[v][j];
f[u][k%M]+=f[v][j];
}
}
}
void dfs2(int u,int fa)//求v的子树以外的边权和
{
for(int i=hd[u];i;i=e[i].nx)
{
int v=e[i].v;if(v==fa)continue;
int tmp=F[u]-F[v]-e[i].w/M;
for(int j=0;j<M;j++)
{
int k=j+e[i].w;
tmp-=k/M*f[v][j];
g[k%M]=f[u][k%M]-f[v][j];
}
g[e[i].w%M]--;
F[v]+=tmp+e[i].w/M;
f[v][e[i].w%M]++;
for(int j=0;j<M;j++)
{
int k=j+e[i].w;
F[v]+=k/M*g[j];
f[v][k%M]+=g[j];
}
dfs2(v,u);
}
}
int main()
{
//freopen("in.txt","r",stdin);
scanf("%d%d",&n,&m);
int u,v,w;
for(int i=1;i<n;i++)
scanf("%d%d%d",&u,&v,&w),add(u,v,w),add(v,u,w);
dfs1(1,0);dfs2(1,0);
for(int i=1;i<=n;i++)
{
int ans=F[i]<<4;//复原那些不会被异或的部分
for(int j=0;j<M;j++)
ans+=(j^m)*f[i][j];
printf("%d\n",ans);
}
return 0;
}
赛后总结
打的很不好。预计分数是100+30+30=160,T1在临提交时发现数组大小开小了,修改时漏掉dp数组,丢30分。T2爆搜可以过30分的,但是只有0分。T3暴力只打了10分。
问题:1.在考虑一个数能不能用4和7的组合表示出来的时候,没有第一时间想到去年DAY1T1小凯的疑惑那题的结论,非常不应该。
2.DP数组没有去修改。
3.暴力水平很低,爆搜完全没得分。
反思