码迷,mamicode.com
首页 > 其他好文 > 详细

11.10晚间练习赛 一套全场爆零的好题

时间:2019-11-11 12:28:03      阅读:90      评论:0      收藏:0      [点我收藏+]

标签:相互   cdn   stdout   mat   顶点   一点   rom   镶嵌   通道   

11.10晚间练习赛 一套全场爆零的好题

nodgd改的题面是真的令人不解

T1 数正方形

题面:
\(N * N\)的点阵中任取4个点,回答:
问题1:这4个点恰好是“正放”的正方形的4个顶点的方案数是多少?
问题2:这4个点恰好是正方形(包括“正放”和“斜放”)的4个顶点的方案数是多少?
下图为一个4*4的点阵,左图表示一种“正放”的方案,右图表示一种“斜放”的方案。
技术图片
技术图片
------

看出来了就是水题
首先你需要在纸上画一画图,把从3~5的方阵中斜放的,且顶点在方阵边界的正方形。
然后你发现,\(N=3\)时有1个;\(N=4\)时有2个;\(N=5\)时三个。
于是规律就出来了。
详情见代码


KONO代码哒!

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
typedef long long ll;
const ll maxn=100010;
const ll mod=1e9+7;
inline ll po(ll x){return (x*x)%mod;}
ll n,k;
ll pfh[maxn];
inline ll ads(ll x,ll y){return ((x%mod)*(y%mod))%mod;}
int main()
{
//  freopen("square.in","r",stdin);
//  freopen("square.out","w",stdout);
    ll i;
    scanf("%lld%lld",&n,&k);
    for(i=1;i<=n;i++)
    {
        pfh[i]=(pfh[i-1]+po(i))%mod;
    }
    if(k==1)printf("%lld\n",pfh[n-1]);
    else{
        ll res=0;
        for(i=3;i<=n;i++){
            res=(res+ads(po(n-i+1),(i-2)))%mod;
        }
        printf("%lld\n",(pfh[n-1]+res)%mod);
        return 0;
    }
}

T2 四叶草魔杖同学之间互传答案

题面:
魔杖护法Freda融合了四件武器,于是魔杖顶端缓缓地生出了一棵四叶草,四片叶子幻发着淡淡的七色光。圣剑护法rainbow取出了一个圆盘,圆盘上镶嵌着N颗宝石,编号为0~N-1。第i颗宝石的能量是Ai。如果Ai>0,表示这颗宝石能量过高,需要把Ai的能量传给其它宝石;如果Ai<0,表示这颗宝石的能量过低,需要从其它宝石处获取-Ai的能量。保证∑Ai =0。只有当所有宝石的能量均相同时,把四叶草魔杖插入圆盘中央,才能开启超自然之界的通道。
不过,只有M对宝石之间可以互相传递能量,其中第i对宝石之间无论传递多少能量,都要花费Ti的代价。探险队员们想知道,最少需要花费多少代价才能使所有宝石的能量都相同?

共有\(n\)个同学传递答案,编号为\(0\)~\(n?1\)。为了避免被老师发现,同学们之间传递的答
案都是一次性的,也就是说一份答案如果传到某个同学处被抄了,那其他同学就不能再抄
这份答案了。
这??个同学中,一些是大佬,大佬不需要抄答案,而且可以传出一些答案;还有一些
是蒟蒻,答案传到蒟蒻手上时,蒟蒻会根据自己的需求抄一些答案,并将剩余的答案传给
其他人。准确的说,第i个同学有一个数值\(A_i\)\(A_i>0\)表示这位同学是大佬,他会传出Ai份
答案;\(A_i<0\)表示这位同学是蒟蒻,他需要抄?\(A_i\)份答案。而且,答案的供需关系是平衡
的,即\(A0+A1+?+An?1=0\)
然而,机房传答案收到很多限制,所以只有m对同学之间可以相互传答案。一对同学
之间传递答案是有代价的:如果整个过程中都不传答案,则代价为 0;如果传了答案,无
传多少份答案,无论传答案是哪个方向,代价都是固定的。
现在,请你计算最少需要多大代价,才能保证答案的正常流通?

\(2<=N<=16,0<=M<=N*(N-1)/2,0<=pi,qi<N,-1000<=Ai<=1000,0<=Ti<=1000,∑Ai=0\)


考试时:
WZJ&Wx2004:会不会是最小生成树?
WZJ:把所有区块和=0的块连起来?
WZJ:不对,区块可能很多,要炸。

讲题时:
WZJ:WDNMD


题解:
最小生成树+状态压缩
想不到吧哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈
一想到每个点值和为0的区块不需要连上其他点值和为0的区块,我们就想到了分成区块来处理。一看到\(n\)不大于16,状态压缩没跑了。
首先设全集为tot,则把tot内所有区块点值的和为0的情况找出来,每个跑一次最小生成树
。把其余不为0的子集dp值设为inf。
然后再枚举tot子集,用\(3^N\)的暴力算法转移。
转移方程为:\(Dp[s]=min \{ Dp[s],Dp[s']+Dp[s\)^$ s‘] }\((其中\)s‘\(为\)s$子集)。
爆不了。
注:在跑最小生成树时要注意判断能否各点之间能否到达,若不能则把该集合的值设为inf
inf要开大。


KONO代码哒!

#include<cstdio> 
#include<iostream> 
#include<cmath> 
#include<cstring> 
#include<algorithm> 
using namespace std; 
//不要用不要用不要用链式! 
char *p1,*p2,buf[1<<20]; 
// #define GC (p1==p2&&(p1=buf,p2=buf+fread(buf,1,1<<20,stdin),p2==p2)?0:(*(p1++))) 
#define GC getchar() 
inline int in() 
{ 
    int w=0,x=0; 
    char ch=0; 
    while(!isdigit(ch)){ 
        w|=ch=='-'; 
        ch=GC; 
    } 
    while(isdigit(ch)) 
    { 
        x=(x<<3)+(x<<1)+(ch^48); 
        ch=GC; 
    } 
    return w? -x:x; 
} 
const int maxn=20; 
const int maxm=1000; 
const int inf=0x3f3f3f3f; 
struct edge{ 
    int from,to,len; 
}g[maxm]; 
bool cmp(edge a,edge b) 
{ 
    return a.len<b.len; 
} 
int cnt; 
int n,m; 
int a[maxn]; 
int Val[1<<maxn]; 
int f[1<<maxn]; 
int dp[1<<maxn]; 
int fa[maxn]; 
void add(int from,int to,int len) 
{ 
    g[++cnt].from=from; 
    g[cnt].len=len; 
    g[cnt].to=to; 
} 
bool x1[maxn]; 
int gf(int x) 
{ 
    if(fa[x]!=x)fa[x]=gf(fa[x]); 
    return fa[x]; 
} 
void mst(int num) 
{ 
    int i,j; 
    int cnt1=0; 
    for(i=0;i<n;i++)fa[i]=i,x1[i]=0; 
    int tmp=num,c1=0; 
    while(tmp){ 
        if(tmp&1)x1[c1]=1,++cnt1; 
        ++c1; 
        tmp=(tmp>>1); 
    } 
    int res=0,k=0; 
    for(i=1;i<=m&&k<cnt1-1;i++){ 
        int u=g[i].from,v=g[i].to; 
        if(!x1[u]||!x1[v])continue; 
        int fx=gf(u),fy=gf(v); 
        if(fx==fy)continue; 
        fa[fy]=fx; 
        res+=g[i].len; 
        k+=1; 
    } 
    if(k!=cnt1-1){ 
        f[num]=inf; 
    } 
    else f[num]=res; 
} 
int main() 
{ 
    memset(f,0x3f,sizeof(f)); 
    memset(dp,0x3f,sizeof(dp)); 
    n=in();m=in(); 
    int i,j; 
    for(i=0;i<n;i++)a[i]=in(); 
    for(i=1;i<=m;i++){ 
        int x=in(),y=in(),z=in(); 
        add(x,y,z); 
    } 
    int tot=(1<<n)-1; 
    for(i=1;i<=tot;i++) 
    { 
        int u=i,c=0; 
        while(u){ 
            if(u&1)Val[i]+=a[c]; 
            u=(u>>1); 
            c+=1; 
        } 
    } 
    sort(g+1,g+m+1,cmp);
    for(i=1;i<=tot;i++) 
    { 
        if(Val[i]==0){ 
            mst(i); 
        } 
    } 
    for(i=1;i<=tot;i++){ 
        dp[i]=f[i]; 
        for (int j = i; j; j = (j - 1) &i){ 
                dp[i]=min(dp[i],dp[j]+dp[i^j]); 
            }   
    } 
    if(dp[tot]!=inf)
    printf("%d\n",dp[tot]);
    else printf("Impossible\n"); 
}

注意有10分的\(Impossible\)点。


T3 金轮法王的龙象般若功却被老师逮个正着

啥玩意儿?!

题面:
同学们在教室里传答案被逮到了,但问题不大,以前被逮到过很多次了。
全班共有个\(N\)同学, 每次参与传答案的同学都是个同学的一个非空子集。 一学期共有
\(2^N\)次考试,每次考试参与传答案的同学都不完全相同。
老师每次考试有一半的概率记录这次考试参与传答案的同学名单,也有一半的概率当
做什么也没发生。到了期末,老师检查一下自己记下的名单,找出每次都参与了传答案的
同学,作为这学期的“始作俑者”。特别的,如果老师一学期没有记录任何一次考试的传答
案名单,则没有学生是“始作俑者”。
老师对第\(i\)个学生的惩罚系数是正整数\(A_i\)。期末抓出始作俑者之后,算出始作俑者人数
\(x\)和最大的惩罚系数\(y\),对全班施加的惩罚量\(z=xy\)特别的,如果没有学生是始作俑者,
惩罚量为0。
求期末惩罚量的期望\(E(z)\),输出\(E(z)*{2^2}^n\%998244353\)。可以证明\(E(z)*{2^2}^n\)为整数。

\(N\leq2^{12}\),\(|A_i|\leq2^{20}\)

我没有什么好讲的。详见wangdy带佬的题解。

技术图片
技术图片
技术图片

总结:

  1. 组合算法就组合算法嘛,打就是了嘛,不要怕嘛
  2. 多想一点算法嘛。不要看着什么就是什么嘛
  3. 不要放掉期望的题,就算不你会做也要骗点分再走嘛
    ?
    ?
    OIer眼中的图片
    技术图片

11.10晚间练习赛 一套全场爆零的好题

标签:相互   cdn   stdout   mat   顶点   一点   rom   镶嵌   通道   

原文地址:https://www.cnblogs.com/cooper233/p/11833866.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!