比完赛了,随便写点解题报告!这次月赛是我们三个人组队,最终4题,结果不好也不坏
题目给出一个有向图,很明显,每个点都有一个状态:在该点处lcm为x对应的个数
1)由于状态转移时lcm必须要变,所以即使存在环,状态也不可能在环内一直不停的转移
2) K <= 10^6 点数为 2000,看起来状态数为2000*10^6 很吓人,其实没那么多,我们只要记录每个点的lcm为K的因子即可,其他的lcm都没用
比赛的时候没有想到第二点优化,也过了,貌似还挺快的,早上交了,在zoj上30ms,真没想到暂列第一
我的使用广搜实现的,据说拓扑排序,记忆化搜索可以搞,Orz
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <map>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
const int maxn=1010;
const int mod=1000000007;
vector<int> g[maxn];
map<int,int> dp[maxn];
int score[maxn],n,K;
bool vis[maxn];
int gcd(int a,int b)
{
return b==0?a:gcd(b,a%b);
}
int bfs()
{
int ret=0;
queue<int> q;
dp[1][score[1]]=1;
memset(vis,0,sizeof(vis));
for(q.push(1);!q.empty();q.pop())
{
int cur=q.front();
//cout<<cur<<endl;
vis[cur]=0;
if(cur==n) ret=(ret+dp[cur][K])%mod;
for(int i=0;i<g[cur].size();i++)
{
int v=g[cur][i];
for(map<int,int>::iterator it=dp[cur].begin();it!=dp[cur].end();it++)
{
int pre=it->first;
int lcm=pre/gcd(pre,score[v])*score[v];
if(lcm<=K&&lcm!=pre){
dp[v][lcm]=(dp[v][lcm]+it->second)%mod;
if(!vis[v]) vis[v]=1,q.push(v);
}
}
}
dp[cur].clear();
}
return ret;
}
int main()
{
int m,u,v;
while(scanf("%d%d%d",&n,&m,&K)==3)
{
while(m--){
scanf("%d%d",&u,&v);
g[u].push_back(v);
}
for(int i=1;i<=n;i++) scanf("%d",&score[i]);
printf("%d\n",bfs());
for(int i=1;i<=n;i++) g[i].clear();
}
return 0;
}
B BiliBili
队友开始用模拟退火搞,由于精度问题没搞出来,之后提示说是解方程,问我会不会,仔细化简后发现高斯肖元可以搞,然后我把各个系数算好,写了个main函数,队友贴了个double 型高斯肖元的模版,然后就水过了
这题能搞出来,纯是yy,队友贴了个最大流模版就过了,看来比赛的时候yy能力也很重要呀,Orz那些能够1y的神,他们应该能够很清楚的证明为什么,Orz
没看,队友搞的
然后就是比赛唯一遗憾的D题了,我搞了半天都没有搞出来,今天早上忽然想到一种方法开始TLE,优化后wa,然后发现思路错了,太弱了……在 这篇博客 的启发下才做出来,灰常感谢
很容易想到: 总定点数(n+1)*(m+1)中选择3个点 - 三点共线的情况
难点在于找出三点共线的个数
1) 水平三点共线
2) 竖直三点共线
3) 斜着(左下,右上),枚举三点共线所占的矩形的大小n*m(1000*1000) , 然后其中两个点肯定要保证在矩形对应的定点上(2种情况),然后对角线上的定点个数就是gcd(n,m)-1 ,这个多试几个矩形就看出来了,然后乘以这样大小的矩形的个数就行了
貌似把1000*1000 以内gcd预处理出来很快很多(200+ms),但是下面代码看起来更容易理解
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <map>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
typedef long long ll;
const int maxn=1010;
int gcd(int a,int b)
{
return b==0?a:gcd(b,a%b);
}
ll get(int n,int m)
{
if(n<m) return 0;
ll ret=1;
for(int i=0;i<m;i++)
ret=ret*(n-i)/(i+1);
return ret;
}
int main()
{
int n,m;
while(scanf("%d%d",&n,&m)==2)
{
ll ans=get((n+1)*(m+1),3);
for(int i=2;i<=n;i++)
for(int j=2;j<=m;j++)
ans-=(ll)(gcd(i,j)-1)*(n-i+1)*(m-j+1)*2;
for(int i=0;i<=n;i++) ans-=get(m+1,3);
for(int i=0;i<=m;i++) ans-=get(n+1,3);
printf("%lld\n",ans);
}
return 0;
}
本文分享了ZOJMonthly September2012的比赛经验及解题思路,包括使用广搜解决Kitty's Game问题,利用高斯消元法解决BiliBili问题,通过最大流算法解决MatrixTransformer问题,以及如何计算GaotheGrid中的共线点数量。
1万+

被折叠的 条评论
为什么被折叠?



