您的位置:首页 > 其它

CONTEST199 [有奖]洛谷8月月赛题解

2015-08-16 22:07 330 查看
http://www.luogu.org/contest/show?tid=199

T1: 集合求和

可以找一下规律,对于一个包含n个元素的集合,每个元素在所有子集中出现的次数均为2^(n-1),将sum乘这个数即可。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#define ll long long
using namespace std;
ll x,s,sum;
int main()
{
s=1;
while (cin>>x) {sum+=x; s<<=1;};
printf("%lld",sum*(s>>1));
return 0;
}


T2: GCD SUM

我们可以用f[i]表示gcd(x,y)=i的数对共有f[i]个。

从n->1枚举,首先将f[i]赋值为(n/i)*(n/i),然后去掉f[2*i],f[3*i]....即可。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#define ll long long
using namespace std;
int n;
ll ans,f[100005];
int main()
{
scanf("%d",&n);
for (int d=n;d>=1;d--)
{
f[d]=1ll*(1ll*n/d)*(n/d);
for (int i=d+d;i<=n;i+=d) f[d]-=f[i];
ans+=f[d]*d;
}
printf("%lld",ans);
}


T3: 看球泡妹子

本着骗分的想法写了个很暴力的dp,f[i][j][k]即前i个比赛选j个目前帅哥值为k,答案即为max{f[m][k][i],c<=i<=sum},特判一下如果最大的k项相加仍<c则无解(否则只有90分)。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#define ll long long
using namespace std;
int n,m,mx,mn,c,sum,ans;
int a[105],b[105],p[105],q[105],s[105];
int f[105][105][2005];
inline int read()
{
int a=0,f=1; char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();}
while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();}
return a*f;
}
inline bool cmp(int a,int b)
{
return a>b;
}
int main()
{
n=read(); m=read(); mx=read(); c=read();
for (int i=1;i<=n;i++) a[i]=read();
for (int i=1;i<=n;i++) b[i]=read();
for (int i=1;i<=m;i++) p[i]=read(),q[i]=read(),s[i]=b[p[i]]+b[q[i]];
sort(s+1,s+m+1,cmp);
for (int i=1;i<=mx;i++) sum+=s[i];
if (sum<c) {printf("-1"); return 0;}
for (int i=1;i<=m;i++)
for (int j=1;j<=mx;j++)
for (int k=mn;k<=2000;k++)
{
f[i][j][k]=f[i-1][j][k];
if (k-b[p[i]]-b[q[i]]>=0) f[i][j][k]=max(f[i][j][k],f[i-1][j-1][k-b[p[i]]-b[q[i]]]+a[p[i]]*a[q[i]]);
}
for (int i=c;i<=2000;i++) ans=max(ans,f[m][mx][i]);
if (!ans) printf("-1"); else printf("%d",ans);
return 0;
}


T3第二种方法:

令x[i]=20-(b[p[i]]+b[q[i]]),y[i]=a[p[i]]*a[q[i]].那么若满足题意就等价于满足sigma(x[i])<20*k-c;这就成了一个二维费用背包。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
using namespace std;
int n,m,K,c,ans;
int a[105],b[105],p[105],q[105],x[105],y[105];
int f[105][2005];
inline int read()
{
int a=0,f=1; char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();}
while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();}
return a*f;
}
int main()
{
n=read(); m=read(); K=read(); c=read(); c=20*K-c;
for (int i=1;i<=n;i++) a[i]=read();
for (int i=1;i<=n;i++) b[i]=read();
for (int i=1;i<=m;i++) {p[i]=read(); q[i]=read(); x[i]=20-b[p[i]]-b[q[i]]; y[i]=a[p[i]]*a[q[i]];}
for (int i=1;i<=m;i++)
for (int j=K;j>=1;j--)
for (int k=c;k>=x[i];k--) f[j][k]=max(f[j][k],f[j-1][k-x[i]]+y[i]);
for (int i=1;i<=c;i++) ans=max(ans,f[K][i]);
if (!ans) printf("-1"); else printf("%d",ans);
return 0;
}


T4: 电路维修

应该是这次比赛比较难的一道题了,我们可以对每条边所在格的四个顶点连边,已相连的权值为0,未相连的权值为1。然后跑最短路就好。

PS:对于判断无解,则为n+m为奇数,画个图即可发现。

PS:我们可以发现有一些点是无论如何都不会走到的,可以忽略这些点,可以省去一半。

PS:spfa80分会TLE,heap+priority_queue可过。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#define ll long long
#define INF 1000000000
#define M 300005
#define pa pair<ll,int>
using namespace std;
int n,m,cnt;
int head[300000],q[300010],dis[300000],next[1200000],list[1200000],key[1200000];
bool v[300000],vis[300000];
priority_queue<pa,vector<pa>,greater<pa> >qq;
inline int read()
{
int a=0,f=1; char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();}
while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();}
return a*f;
}
inline void insert(int x,int y,int z)
{
next[++cnt]=head[x];
head[x]=cnt;
list[cnt]=y;
key[cnt]=z;
}
/*inline int spfa()
{
for (int i=1;i<=n*m;i++) {dis[i]=INF; v[i]=0;}
int t=0,w=1,x; dis[1]=0; v[1]=1; q[1]=1;
while (t!=w)
{
t=(t+1)%M;
x=q[t];
for (int i=head[x];i;i=next[i])
if (dis[list[i]]>dis[x]+key[i])
{
dis[list[i]]=dis[x]+key[i];
if (!v[list[i]])
{
v[list[i]]=1;
w=(w+1)%M;
q[w]=list[i];
}
}
v[x]=0;
}
return dis[n*m];
}*/
inline int dijkstra()
{
for(int i=1;i<=m*n;i++)dis[i]=INF;dis[1]=0;
memset(vis,0,sizeof(vis));
qq.push(make_pair(0,1));
while(!qq.empty())
{
int now=qq.top().second;qq.pop();
if(vis[now])continue;vis[now]=1;
for(int i=head[now];i;i=next[i])
if(dis[now]+key[i]<dis[list[i]])
{
dis[list[i]]=dis[now]+key[i];
qq.push(make_pair(dis[list[i]],list[i]));
}
}
return dis[n*m];
}
int main()
{
int t=read();
while (t--)
{
n=read()+1; m=read()+1;
if ((n+m)%2==1) {puts("NO SOLUTION"); continue;}
for (int i=1;i<n;i++)
{
char c[505];
scanf("%s",c+1);
if (i%2==1)
{
for (int j=1;j<m;j+=2)
{
if (c[j]!='/') {insert((i-1)*m+j,i*m+j+1,0); insert(i*m+j+1,(i-1)*m+j,0);}
else {insert((i-1)*m+j,i*m+j+1,1); insert(i*m+j+1,(i-1)*m+j,1);}
}
for (int j=2;j<m;j+=2)
{
if (c[j]!='/') {insert(i*m+j,(i-1)*m+j+1,1); insert((i-1)*m+j+1,i*m+j,1);}
else {insert(i*m+j,(i-1)*m+j+1,0); insert((i-1)*m+j+1,i*m+j,0);}
}
}
if (i%2==0)
{
for (int j=1;j<m;j+=2)
{
if (c[j]!='/') {insert(i*m+j,(i-1)*m+j+1,1); insert((i-1)*m+j+1,i*m+j,1);}
else {insert(i*m+j,(i-1)*m+j+1,0); insert((i-1)*m+j+1,i*m+j,0);}
}
for (int j=2;j<m;j+=2)
{
if (c[j]!='/') {insert((i-1)*m+j,i*m+j+1,0); insert(i*m+j+1,(i-1)*m+j,0);}
else {insert((i-1)*m+j,i*m+j+1,1); insert(i*m+j+1,(i-1)*m+j,1);}
}
}
}
printf("%d\n",dijkstra());
for (int i=1;i<=n*m;i++) head[i]=0; cnt=0;
}
return 0;
}


第一次AK……纪念一下。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: