PKUSC预热__被水题虐TAT

感觉最近有些不在状态,几场考试考的都不是非常好.

然后我这种大弱渣显然只能去P啦~~~

(虽然觉得ACM赛制还是要跪

不过总要做出选择吗.

POJ1832

首先递推算出fi表示第i位到第0位全部变成0的最小移动次数.

gi表示将第i位变成1,同时第i?1位到第0位都变成0的最小移动次数.

利用数学归纳法可以得到:

若有一个在第i位的1,将它变成0需要的最小步数为2i+1?1.

从而递推式就很容易写出来了.(我太懒就略过了

然后我们可以模拟变的过程,首先找到最高的不相同的位,然后对后面调用g,然后后面就变成了10000000...这样的情况,再从高到低依次模拟就行了.

显然需要高精度.

另外还有一种黑科技就是求出格雷码并转化为十进制数直接求差的绝对值即可.

#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
using namespace std;

static const int mod=1e4;
struct Hugeint{
    int d[1000],l;
    Hugeint():l(1){
        memset(d,0,sizeof d);
    }

    inline void operator*=(const int&x){
        int t=0;
        for(int i=0;i<l;++i){
            t+=d[i]*x;
            d[i]=t%mod;
            t/=mod;
        }
        if(t)
            d[l++]=t;
    }
    inline void operator+=(const Hugeint&B){
        l=l>B.l?l:B.l;
        for(int i=0;i<l;++i){
            d[i]+=B.d[i];
            if(d[i]>=mod){
                d[i]-=mod;
                d[i+1]++;
            }
        }
        if(d[l])
            ++l;
    }
    inline void output(){
        printf("%d",d[l-1]);
        for(int i=l-2;i>=0;--i)
            printf("%04d",d[i]);
    }
};

Hugeint pow[130];

int a[128],b[128];
Hugeint f[130],g[130];

int main(){
    //freopen("tt.in","r",stdin);
    int T,n,i,j;
    pow[0].d[0]=1;
    for(i=1;i<128;++i){
        pow[i]=pow[i-1];
        pow[i]*=2;
    }
    cin>>T;
    for(int Tcase=1;Tcase<=T;++Tcase){
        cin>>n;
        for(i=n-1;i>=0;--i)
            cin>>a[i];
        for(i=n-1;i>=0;--i)
            cin>>b[i];

        f[0].d[0]=g[0].d[0]=0;
        if(a[0]==0)
            g[0].d[0]=1;
        else
            f[0].d[0]=1;
        for(i=1;i<n;++i){
            if(a[i]==0){
                f[i]=f[i-1];
                g[i]=g[i-1],g[i]+=pow[i];
            }
            else{
                f[i]=g[i-1],f[i]+=pow[i];
                g[i]=f[i-1];
            }
        }
        Hugeint res;
        int ins=-1;
        for(i=n-1;i>=0;--i)
            if(a[i]!=b[i]){
                ins=i;
                break;
            }
        if(ins<0)
            puts("0");
        else{
            if(ins)
                res+=g[ins-1];
            res+=pow[0];
            if(ins>0){
                memset(a,0,sizeof a);
                a[ins-1]=1;
                for(i=ins-1;i>=0;--i){
                    if(a[i]!=b[i]){
                        res+=pow[i];
                        if(i)
                            a[i-1]=1;
                    }
                }
            }
            res.output();
            puts("");
        }
    }
    return 0;
}

POJ1112

首先建立反图,若两个点之间有连边则证明不能在一个集合中.

这让我们联想到二分图.

于是对于每一个联通分量进行二染色,若存在一个连通分量不能二染色则无解.

注意题目让我们找出一组最接近的解.

对于每个联通分量,我们将染色的方案存下来,那么有用的仅仅是两个部分分别属于哪个集合.

我们做一次简单的dp并记录方案即可.

#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;

#define N 110
int head[N],next[N*N<<1],end[N*N<<1];
inline void addedge(int a,int b){
    static int q=1;
    end[q]=b;
    next[q]=head[a];
    head[a]=q++;
}
inline void make(int a,int b){
    addedge(a,b);
    addedge(b,a);
}

bool G[N][N];

int v[N];

int cnt;
vector<int>s[N][2];
bool f[N][N],g[N][N];

inline bool dfs(int x){
    if(v[x]==-1){
        v[x]=1;
        s[cnt][1].push_back(x);
    }
    for(int j=head[x];j;j=next[j]){
        if(v[end[j]]==-1){
            v[end[j]]=1-v[x];
            s[cnt][1-v[x]].push_back(end[j]);
            if(!dfs(end[j]))
                return 0;
        }
        else if(v[end[j]]==v[x])
            return 0;
    }
    return 1;
}
inline int _abs(int x){
    return x<0?-x:x;
}

bool ok[N];

inline void find(int x,int y){
    if(x==0)
        return;
    if(g[x][y]==0){
        for(int i=0;i<s[x][0].size();++i)
            ok[s[x][0][i]]=1;
        find(x-1,y-s[x][0].size());
    }
    else{
        for(int i=0;i<s[x][1].size();++i)
            ok[s[x][1][i]]=1;
        find(x-1,y-s[x][1].size());
    }
}

int main(){
    int n;
    scanf("%d",&n);
    int i,j,x;
    for(i=1;i<=n;++i){
        while(scanf("%d",&x)&&x)
            G[i][x]=1;
    }
    for(i=1;i<=n;++i)
        for(j=i+1;j<=n;++j)
            if(!(G[i][j]&&G[j][i]))
                make(i,j);
    memset(v,-1,sizeof v);
    bool nosol=0;
    for(i=1;i<=n;++i){
        if(v[i]==-1){
            ++cnt;
            if(!dfs(i)){
                nosol=1;
                break;
            }
        }
    }
    if(nosol)
        puts("No solution");
    else{
        f[1][s[1][1].size()]=1;
        g[1][s[1][1].size()]=1;
        f[1][s[1][0].size()]=1;
        g[1][s[1][0].size()]=0;
        for(i=1;i<cnt;++i)
            for(j=0;j<=n;++j)
                if(f[i][j]){
                    f[i+1][j+s[i+1][0].size()]=1;
                    g[i+1][j+s[i+1][0].size()]=0;
                    f[i+1][j+s[i+1][1].size()]=1;
                    g[i+1][j+s[i+1][1].size()]=1;
                }
        int ans=0x3f3f3f3f,num;
        for(i=0;i<=n;++i){
            if(f[cnt][i]&&_abs(i-(n-i))<ans){
                ans=_abs(i-(n-i));
                num=i;
            }
        }
        find(cnt,num);
        vector<int>v1,v2;
        for(i=1;i<=n;++i)
            if(ok[i])
                v1.push_back(i);
            else
                v2.push_back(i);
        printf("%d",v1.size());
        for(i=0;i<v1.size();++i)
            printf(" %d",v1[i]);
        puts("");
        printf("%d",v2.size());
        for(i=0;i<v2.size();++i)
            printf(" %d",v2[i]);
    }
    return 0;
}

POJ2238

题目水的一比,只需要暴力枚举自己的四项属性,然后做一次dp看一看此时的获胜概率是多少就行了.

令fi,j,0,fi,j,1分别表示自己得到i分,另一个人得到j分,下一次自己/别人出手的概率.

把fi,j,0,fi,j,1缩成一个强连通分量,不难发现状态转移图是一个拓扑图.

于是只需要套用一般的方法:对于每一个连通分量先处理这个连通分量里面的答案,然后将概率转移到后续的连通分量里面的点,然后再后面的连通分量里面再处理就行了.

对于一个连通分量,里面的转移是存在环的,因此要列方程进行求解.

对于分数二元组(i,j),令p0表示fi,j,0,令p1表示fi,j,1,则有以下的方程:

p0=stay0p0+change1p1+c0

p1=stay1p1+change0p0+c1

其中c0,c1表示从上面的连通分量转移下来的贡献.

随便解个方程就行了.

现在问题的关键是源点怎么转移.

(跪大爷

我们可以在两个方程中的一个加上p0=1,这样我们惊奇的发现得到正确的答案啦!

但是这样转移下去是并不对的!

上述的正确答案是指只有这个连通分量时的正确答案.

也就是说源点本可以以更大的概率转移到下面的分量.

因此我们需要将这个连通分量里面所有的点的概率都除以原点的概率!(雾

其实我得出的结论是:只要方程是对的,随便搞搞就能得出正确的结果啦!

然后也很容易对拍:不断迭代就行啦!

#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;

typedef double db;
int n,m;
int pt2[2],pt3[2],reb[2],def[2];

db ans=0;

db f[35][35][2],g[35][35][2];
db ok3[2],ok2[2],stay[2],change[2];

typedef pair<int,int> pii;
int deg[35][35];
bool vis[35][35];

static const int dx[4]={2,0,3,0};
static const int dy[4]={0,2,0,3};

pair<db,db>solve(db a1,db b1,db c1,db a2,db b2,db c2){
    db x,y;
    y=(c1*a2-c2*a1)/(b1*a2-a1*b2);
    x=(c1-b1*y)/a1;
    return make_pair(x,y);
}

inline db dp(){
    int i,j,k;
    for(i=0;i<2;++i){
        db p=pt3[i]/(db)(pt2[i]+pt3[i]);
        ok3[i]=p*0.8*pt3[i]/(db)(pt3[i]+def[1-i]);
        ok2[i]=(1-p)*pt2[i]/(db)(pt2[i]+def[1-i]);
        stay[i]=(1-ok3[i]-ok2[i])*0.8*reb[i]/(db)(reb[0]+reb[1]);
        change[i]=1-ok3[i]-ok2[i]-stay[i];
    }

    queue<pii>q;
    q.push(make_pair(0,0));
    memset(vis,0,sizeof vis);
    vis[0][0]=1;
    memset(deg,0,sizeof deg);
    pii tmp;
    while(!q.empty()){
        tmp=q.front();
        q.pop();
        int x=tmp.first,y=tmp.second;
        if(x>=n||y>=n)
            continue;
        for(i=0;i<4;++i){
            ++deg[x+dx[i]][y+dy[i]];
            if(!vis[x+dx[i]][y+dy[i]]){
                vis[x+dx[i]][y+dy[i]]=1;
                q.push(make_pair(x+dx[i],y+dy[i]));
            }
        }
    }

    memset(f,0,sizeof f);
    memset(g,0,sizeof g);
    db re=0;
    q.push(make_pair(0,0));
    while(!q.empty()){
        tmp=q.front();
        q.pop();
        int x=tmp.first,y=tmp.second;
        pair<db,db>get;
        if(x==0&&y==0){
            get=solve(2-stay[0],-change[1],1,-change[0],1-stay[1],0);
            get.second/=get.first;
            get.first=1;
        }
        else
            get=solve(1-stay[0],-change[1],g[x][y][0],-change[0],1-stay[1],g[x][y][1]);
        f[x][y][0]=get.first;
        f[x][y][1]=get.second;

        if(x>=n||y>=n)
            continue;
        g[x+2][y][0]+=f[x][y][0]*ok2[0];
        g[x][y+2][1]+=f[x][y][1]*ok2[1];
        g[x+3][y][0]+=f[x][y][0]*ok3[0];
        g[x][y+3][1]+=f[x][y][1]*ok3[1];

        for(i=0;i<4;++i)
            if(!(--deg[x+dx[i]][y+dy[i]]))
                q.push(make_pair(x+dx[i],y+dy[i]));
    }

    for(j=0;j<=34;++j)
        for(k=0;k<=34;++k)
            if((j>=n||k>=n)&&(j>k))
                re+=f[j][k][0];
    return re;

}

inline void dfs(int dep,int last){
    if(dep==5&&last==0){
        ans=max(ans,dp());
        return;
    }
    for(int i=1;i<=10&&i<=last;++i){
        if(dep==1)
            pt2[0]=i;
        else if(dep==2)
            pt3[0]=i;
        else if(dep==3)
            reb[0]=i;
        else
            def[0]=i;
        if(dep==3){
            if(last-i>=1&&last-i<=10)
                dfs(dep+1,last-i);
        }
        else
            dfs(dep+1,last-i);
    }
}

int main(){
    while(scanf("%d%d%d%d%d%d",&n,&m,&pt2[1],&pt3[1],&reb[1],&def[1])!=EOF){
        ans=0;
        dfs(1,m);
        printf("%.3lf\n",ans);
    }
    return 0;
}
时间: 2024-10-11 16:40:19

PKUSC预热__被水题虐TAT的相关文章

HDU ACM 4847 Wow! Such Doge! 被水题坑了

分析:水题,题目居然这么长,全国邀请赛也有水题?strlen(a)返回的是无符号整形,strlen(a)-4会变为正的很大的数,还被RE了两次,唉!人老了.转换为int即可. #include<iostream> using namespace std; #define N 1000010 int main() { char a[N+10]; int ans,i; ans=0; while(gets(a)) { for(i=0;i<strlen(a);i++) if(a[i]>='

OpenJudgeP1.7.10:简单密码__(刷题)_水题

因为本人很懒,不想去找什么,ASCII码的规律...... 就又开了一个cpp,打了一份if&else 的表. 1 #include <bits/stdc++.h> 2 using namespace std; 3 char a[50]; 4 char b[50]; 5 6 /*A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 7 V W X Y Z A B C D E F G H I J K L M N O P Q R S T U

RQNOJ144伯虎点秋香(水)

又是一道水题,转语言就是这样,经常被水题虐翻... 这种水题似乎不适合写出来,不过也没办法,没题目交了字符串处理:           RQNOJ144 伯虎点秋香 题目描述 题目描述: 上次唐伯虎点秋香那是秋香自己跑出来滴,这次他可要真的自己点了,下面由我来描述一下点的规则.当然事先要列举所有姑娘的匿名咯,我要对他进行1次提问,我问他对第几个姑娘感兴趣,他选的当然是他的意中人咯. 输入格式 第一行为一个整数小于1001的整数n,k.从第二行到第n+1行是对每个姑娘的名字. 输出格式 输出文件有

膜拜acm大牛 虽然我不会这题,但是AC还是没有问题的~(转自hzwer)

wywcgs: 亦称Lord Wu,俗名吴垠,2009级厦门大学智能科学与技术学院研究生,本科就读于哈尔滨工业大学.因其深厚的算法功底与独到的思维方式,被尊为“吴教主”,至今声威犹存. 2006年起参加ACM/ICPC竞赛,获得分别获得上海.西安.长春三枚区域赛银牌. 2008年获得GCJ-BeiJing Onsite参赛资格,并在比赛中表现优异,获得Final资格,后因个人原因,推掉了总决赛资格,并忙于为各赛区出题. 2010年复出比赛,在福建省举办的全国邀请赛中力压群牛,获得赛区金牌,给同场

HDU 5427 A problem of sorting 水题

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5427 A problem of sorting Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 1447    Accepted Submission(s): 554 Problem Description There are many p

【学长虐学弟欢乐赛系列】Round4

第四套题 出题人:Bakser 神犇地址:Bakser学长的blog 试题下载地址 数据下载地址 T1 LICS 有一天 Bakser 在做数学题,对于一道题他验算了 n 遍,得到了 n 个结果.无聊的他将这 n 个结果排成了一个圆环,无聊的他又想求这 个环的最长上升子序列. 请你回答这个无聊的问题. 所谓环的上升子序列,指的是从某个位置开始, 按照顺时针顺序 读这个环,得到一个线性序列,它的上升子序列也是这个环的上升子 序列.最长上升子序列是它们中最长的一个. 输入格式: 第一行一个数 n,表

2018暑期做题部分整合

<Matrix>(HDU) 题意:n*m矩阵,每个点可黑可白,问有多少种方案使矩阵至少有A行B列全黑. 思路:第一反应当然是容斥,但是发现a+1行全黑的方案,并不是恰被a行全黑的方案多算a次,所以直接+1,-1,+1,-1这样的容斥系数就不可行. 而如果DP,复杂度太高,不可行. 于是考虑手推容斥系数,a行全黑的方案,被计数的次数取为([a>=A]-被更小的a计算次数)即可. 收获:对于复杂的计数问题,如果分类时,一种方案会在若干类中重复计数,可以使用推广的容斥来做. <Alway

[bzoj3217]ALOEXT

被这题虐了快两天............ 找最大的异或值显然用trie..因为还要支持插入删除修改..所以就用平衡树套trie. 如果旋转的话,整颗trie都要重新建,所以正常姿势是替罪羊树(虽然只是早建晚建的区别= =)? 看了学长的解题报告后才敢用treap= =..结果就陷入了无尽的调试中TAT..代码能力还是拙计...前前后后改了两天,大概得有8h+吧... 具体做法:每个treap的节点上建一颗高度为20的trie(因为数的大小<2^20),在trie中插入treap节点所在子树中的所

POJ 2060 Taxi Cab Scheme【最小路径覆盖】

T - Taxi Cab Scheme Time Limit:1000MS     Memory Limit:30000KB     64bit IO Format:%I64d & %I64u Submit Status Practice POJ 2060 Appoint description:  System Crawler  (2014-08-22) Description Running a taxi station is not all that simple. Apart from