您的位置:首页 > 其它

ZOJ Monthly, September 2012

2012-09-30 12:31 375 查看
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;
}


B BiliBili

队友开始用模拟退火搞,由于精度问题没搞出来,之后提示说是解方程,问我会不会,仔细化简后发现高斯肖元可以搞,然后我把各个系数算好,写了个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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: