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 ",dijkstra()); for (int i=1;i<=n*m;i++) head[i]=0; cnt=0; } return 0; }
第一次AK……纪念一下。