ZOJ Monthly, September 2012
比完赛了,随便写点解题报告!这次月赛是我们三个人组队,最终4题,结果不好也不坏
A Kitty's Game
题目给出一个有向图,很明显,每个点都有一个状态:在该点处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; }
队友开始用模拟退火搞,由于精度问题没搞出来,之后提示说是解方程,问我会不会,仔细化简后发现高斯肖元可以搞,然后我把各个系数算好,写了个main函数,队友贴了个double 型高斯肖元的模版,然后就水过了
C Matrix Transformer
这题能搞出来,纯是yy,队友贴了个最大流模版就过了,看来比赛的时候yy能力也很重要呀,Orz那些能够1y的神,他们应该能够很清楚的证明为什么,Orz
KLetty's Math Class
没看,队友搞的
D Gao the Grid
然后就是比赛唯一遗憾的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; }