雅礼学习10.3

各题状况

T1

暴力修改+一维差分+二维差分

莫名其妙就没了49分。。。

好像是数组开的不够大?

T2

这。。。概率和期望,一会不会,连那个一分的部分分都没有任何思路

T3

题目并没有看太懂。。

写了一个枚举算法,然后对某个一分的数据输出显然的结果

。。。

然后就只拿了1分

枚举挂了,因为会错了题目含义

题目及考场代码

T1

/*
 * 一个个修改肯定超时。。
 * q==0的直接输出0
 * 19分应该是暴力
 *
 * 考虑对每次操作,计算一共修改了多少个位置
 * 奇数个的话就让当前答案异或这个数字
 * 对于边界单独讨论
 * 但是问题在于。。。如果修改的位置刚好把当前答案所在位置变大了,就不应该是异或,而是+
 * 真的不会维护啊
 * 假装这么写是正确的吧。。。
 *
 * 每行差分
 * 最后O(n^2)统计
 */
#include <cstdio>

inline int read()
{
    int n=0,w=1;register char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
    while(c>='0'&&c<='9')n=n*10+c-'0',c=getchar();
    return n*w;
}
inline int min(int x,int y)
{return x<y?x:y;}

const int N=1002;
long long map[N][N],add[N][N],cut[N][N];

int main()
{
    freopen("u.in","r",stdin);
    freopen("u.out","w",stdout);
    int n=read(),q=read(),r,c,s,l,x;
    long long ans=0;
    if(q==0)
    {
        printf("0");
        goto E;
    }
    if(q<=400)
    {
        while(q--)
        {
            r=read(),c=read(),l=read(),s=read();
            x=min(r+l-1,n);
            for(int i=r;i<=x;++i)
                for(int j=c;j<=min(i-r+c,n);++j)
                    map[i][j]+=s;
        }
        for(int i=1;i<=n;++i)
            for(int j=1;j<=n;++j)
                ans^=map[i][j];
    }
    else
        if(q<=2000)
        {
            while(q--)
            {
                r=read(),c=read(),l=read(),s=read();
                x=min(r+l-1,n);
                for(int i=r;i<=x;++i)
                    map[i][c]+=s,map[i][min(i-r+c,n)+1]+=-s;
            }
            for(int i=1;i<=n;++i)
            {
                x=0;
                for(int j=1;j<=n;++j)
                {
                    x+=map[i][j];
                    printf("%d ",x);
                    ans^=x;
                }
                puts("");
            }
        }
        else
        {
            while(q--)
            {
                r=read(),c=read(),l=read(),s=read();
                add[r][c]+=s,add[r+l][c]-=s;
                cut[r][c+1]+=s,cut[r+l][c+l+1]-=s;
            }
            for(int i=1;i<=n;++i)
                for(int j=1;j<=n;++j)
                {
                    add[i][j]+=add[i-1][j];
                    cut[i][j]+=cut[i-1][j-1];
                }
            for(int i=1;i<=n;++i)
                for(int j=1;j<=n;++j)
                {
                    map[i][j]+=map[i][j-1]+add[i][j]-cut[i][j];
                    ans^=map[i][j];
                }
        }

    /*
    else
    {
        while(q--)
        {
            r=read(),c=read(),l=read(),s=read();
            x=l+1;
            if(r+l-1>n || c+l-1>n)
            {//想了半天没想出来怎么快速把多余的部分删掉。
                x=0;
                for(int i=r;i<=min(r+l-1,n);++i)
                    x+=min(i-r+c,n);
            }
            if(x&1)ans^=s;
        }
    }*/
    printf("%lld",ans);

E:  fclose(stdin);fclose(stdout);
    return 0;
}

T2

/*
 * 概率。。。
 * 根本一会不会啊。。。
 * 骗分走人
 */
#include <cstring>
#include <cstdio>

const int N=31;
char s[N];

int main()
{
    freopen("v.in","r",stdin);
    freopen("v.out","w",stdout);

    scanf("%d%d",&n,&k);
    scanf("%s",s);
    int x=0;
    for(int i=0;i<n;++i)
        if(s[i]=='W')
            ++x;
    if(k==0 || k==n)
        printf("%.10lf",(double)1.0*k);
    else    printf("%.10lf",(double)x*1.0/2);

    fclose(stdin);fclose(stdout);
    return 0;
}

T3

/*
 * 输出0有1分。
 */
#include <cstdio>
#include <vector>

int n,step,cnt,k_2,z_2,ku;
int l_note[100023];
struct note{
    int con,have,wash;
};
bool dis[100023];
std::vector<note> tree[100023];
std::vector<int> kuai[100023];
void search(int num)
{
    for(int space,i=0;i<l_note[num];i++)
    {
        space=tree[num][i].con;
        if(((tree[num][i].wash!=tree[num][i].have)||(tree[num][i].wash==2))&&dis[space]!=true)
        {
            tree[num][i].have=tree[num][i].wash;
            ++cnt;
            dis[num]=true;
            search(space);
            break;
        }
    }
}
bool vis[100023];
void qk(int num)
{
    vis[num]=true;
    for (int i=0;i<kuai[num].size();++i)
    {
        int space=kuai[num][i];
        if(tree[num][i].wash==2)++k_2;
        if(vis[space]!=true)
            qk(space);
    }
}
int main ()
{
    freopen("w.in","r",stdin);
    freopen("w.out","w",stdout);
    scanf("%d",&n);
    if(n<=1000)
    {
        for(int a,b,c,d,i=1;i<n;++i)
        {
            scanf("%d%d%d%d",&a,&b,&c,&d);
            tree[a].push_back((note){b,c,d});
            ++l_note[a];
            tree[b].push_back((note){a,c,d});
            ++l_note[b];
            if((c!=d)||(d==2))
            {
                kuai[a].push_back(b);
                kuai[b].push_back(a);
            }
            if(d==2)++z_2;
        }
        for(int i=1;i<=n;++i)
        {
            for(int o=0;o<l_note[i];o++)
            {
                if((tree[i][o].wash!=tree[i][o].have)&&tree[i][o].wash!=2)
                {
                    dis[tree[i][o].con]=true;
                    search(i);
                    break;
                }
            }
        }
        for(int i=1;i<=n;++i)
            if(vis[i]!=true&&kuai[i].size()!=0)
            {
                ++ku;
                qk(i);
            }
        step=ku-(z_2-k_2/2);
        printf("%d %d",step,cnt);
    }
    else    printf("0 0");
    fclose(stdin);fclose(stdout);
    return 0;
}

正解

T1

第一眼\(n\times q\)做法,\(3e8\)显然不能过

那么考虑对其进行优化:差分序列的访问时不连续的,所以我们考虑把它变成连续的

就过了。。。

官方正解:

竖着做一次差分,斜着做一次差分,那么一个三角形就可以确定出来了,而后\(n^2\)扫一遍求异或和

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn=1e3+10;
int n,q;
ll a[maxn][maxn],b[maxn][maxn],ans;

inline void add_a(int x,int y,int v){
    if(x<=n&&y<=n)
        a[x][y]+=v;
}
inline void add_b(int x,int y,int v){
    if(x<=n&&y<=n)
        b[x][y]+=v;
}

int main(){
    freopen("u.in","r",stdin);
    freopen("u.out","w",stdout);
    scanf("%d%d",&n,&q);
    while(q--){
        int r,c,l,s;
        scanf("%d%d%d%d",&r,&c,&l,&s);
        add_a(r,c,s);
        add_a(r+l,c+l,-s);
        add_b(r+l,c,-s);
        add_b(r+l,c+l,s);
    }
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j){
            if(i>1)
                a[i][j]+=a[i-1][j-1]+a[i-1][j]-a[i-2][j-1];
            else
                a[i][j]+=a[i-1][j-1]+a[i-1][j];
            b[i][j]+=b[i-1][j]+b[i][j-1]-b[i-1][j-1];
            ans^=a[i][j]+b[i][j];
        }
    printf("%lld\n",ans);
    return 0;
}

T2

可以得到一个\(O(2^n\times n)\)的状压\(DP\)的做法,记录每个球是否还没有被移除,然后按照最优策略期望移除白球数

事实上有很多重复状态,也就是剩下的求的颜色序列相同时结果是一样的

考虑将状态记成剩下的颜色序列,长度较小的时候就直接用数组去存,较大的时候用\(map\)去存

状态个数能得到一个上界是\(\sum_{i=0}^n\min\{2^i,{i\choose n}\}\)(事实上最大值为\(\sum_{i=1}^{n+1}Fib_i\)但是这一点也不\(noip\)

#include<bits/stdc++.h>
using namespace std;

const int maxn=30+5;
int n,k;
char s[maxn];

namespace ${
    const int xxx=24;
    double a[1<<xxx+1];
    map<int,double> m[maxn];
    inline void init(){
        for(int i=0;i<1<<xxx+1;++i)
            a[i]=-1;
    }
    inline bool count(int bit,int len){
        if(len<=xxx)
            return a[1<<len|bit]!=-1;
        else
            return m[len].count(bit);
    }
    inline double&find(int bit,int len){
        if(len<=xxx)
            return a[1<<len|bit];
        else
            return m[len][bit];
    }
}
inline int erase(int bit,int k){
    return bit&(1<<k)-1|bit>>1&-1<<k;
}
inline double max_(double a,double b){
    return a>=b?a:b;
}
double dfs(int bit,int len){
    if(len<=k)
        return 0;
    if($::count(bit,len))
        return $::find(bit,len);
    double&res=$::find(bit,len);
    res=0;
    for(int i=0,j=len-1;i<=j;++i,--j)
        if(i<j)
            res+=max_(dfs(erase(bit,i),len-1)+(bit>>i&1),dfs(erase(bit,j),len-1)+(bit>>j&1))*2;
        else
            res+=dfs(erase(bit,i),len-1)+(bit>>i&1);
    return res/=len;
}

int main(){
    freopen("v.in","r",stdin);
    freopen("v.out","w",stdout);
    $::init();
    scanf("%d%d%s",&n,&k,s);
    k=n-k;
    int bit=0;
    for(int i=0;i<n;++i)
        bit|=(s[i]=='W')<<i;
    printf("%.10f\n",dfs(bit,n));
    return 0;
}

T3

如果最后翻转的边集是\(S\),最少操作数为\(\{V,S\}\)中奇数度数的点的一半,最小操作总长度是\(|S|\)

考虑树形\(DP\),\(dp[i][0/1]\)记录以\(i\)为根的子树内,\(i\)与父亲之间的边是否翻转,最少的奇数度数的点的个数,此时的最小总长度

#include<bits/stdc++.h>
using namespace std;

const int maxn=1e5+10;
const pair<int,int> inf=make_pair(1e9,1e9);
int n;
vector<pair<int,int> > g[maxn];
pair<int,int> dp[maxn][2];

inline pair<int,int> operator+ (pair<int,int> a,pair<int,int> b){
    return make_pair(a.first+b.first,a.second+b.second);
}
void dfs(int pos,int fa,int type){
    pair<int,int> tmp0(0,0),tmp1(inf);
    for(int i=0,v;i<g[pos].size();++i)
        if((v=g[pos][i].first)!=fa){
            dfs(v,pos,g[pos][i].second);
            pair<int,int> nxt0,nxt1;
            nxt0=min(tmp0+dp[v][0],tmp1+dp[v][1]);
            nxt1=min(tmp1+dp[v][0],tmp0+dp[v][1]);
            tmp0=nxt0;tmp1=nxt1;
        }
    if(type==0||type==2)
        dp[pos][0]=min(tmp0,make_pair(tmp1.first+1,tmp1.second));
    else
        dp[pos][0]=inf;
    if(type==1||type==2)
        dp[pos][1]=min(make_pair(tmp0.first+1,tmp0.second+1),make_pair(tmp1.first,tmp1.second+1));
    else
        dp[pos][1]=inf;
}

int main(){
    freopen("w.in","r",stdin);
    freopen("w.out","w",stdout);
    scanf("%d",&n);
    for(int i=1,a,b,c,d;i<n;++i){
        scanf("%d%d%d%d",&a,&b,&c,&d);
        if(d!=2)
            d=(c!=d);
        g[a].push_back(make_pair(b,d));
        g[b].push_back(make_pair(a,d));
    }
    dfs(1,0,0);
    printf("%d %d\n",dp[1][0].first/2,dp[1][0].second);
    return 0;
}

下午讲课:搜索与剪枝

主要是题目只有题目

例一

有一个\([1,2^n]\)的排列\(A\{1,\cdots,2^n\}\)

可以执行的操作有\(n\)种,每种操作最多可以执行一次。第\(i\)种操作:将序列从左到右划分为\(2^{n-i+1}\)段,每段恰好包括\(2^{i-1}\)个数,然后整体交换其中两段。
求可以将数组\(A\)从小到大排序的不同的操作序列有多少个。
两个操作序列不同,当且仅当操作个数不同,或者至少一个操作不同(种类不同或者操作位置不同)。

\(n\le 12\)

解:

首先,任意一个合法的操作序列,我们可以改变其顺序,依然满足条件。
那么这一类的操作序列的贡献,即为操作次数的阶乘。
那么,现在只考虑种类编号递增的操作序列。第\(i\)种操作时,序列分成了大小为\(2^{i-1}\)的段,如果某个段不是递增且连续的,那么最后肯定不会满足条件。所以,在这种操作考虑完后,每个大小为\(2^i\)的段应当递增且连续。
当考虑第\(i\)种操作时:

  • 如果不合条件的\(2^i\)大小的段超过\(2\)个,直接退出
  • 如果不存在的话,直接继续
  • 如果只有\(1\)个,交换其包含的\(2^{i-1}\)的两段,判断是否可行
  • 如果有\(2\)个就分别搜索\(4\)种交换方案

例二

农夫约翰需要一些特定规格的木材(共\(n\)块,长度不一定相同),可是他只剩下一些大规格的木板(共\(m\)块,长度不一定相同)。不过约翰可以将这些木板切割成他所需要的规格。
求约翰最多能够得到多少他所需要的木材。

\(n\le 1000,m\le 50,lenth\le 32767\)

解:

显然可以二分答案。
判断答案为\(k\)是否可行,显然可以将木材排序,搜索最短的k 块木材是否
能得到。
然后剪枝:

  • 从小到大搜索所需的木材从哪块木板得到,因为长的木材限制比较大
  • 如果两块所需木材长度相同,后搜索的那一块的来源只需要从前者的来源开始枚举
  • 当一块原料的长度比最短的需求还短,那么直接丢弃,如果丢弃总量+所需总量\(\gt\)原料总量,剪掉

例三

给出一个数字\(S\),输出所有约数和等于\(S\)的数

一共\(T\)次询问

\(S\le 2e9,T\le 100\)

解:

\(n=\prod_i p_i^{a^i}\Rightarrow \sigma(n)=\prod_i\sum_{j=0}^{a_i}p_i^j\)

那么我们可以通过枚举\(p_i\)及其\(a_i\)来搜索

若当前需要得到的\(S\)可以表示为为一个未搜索过的质数与\(1\)的和,那么之前的数与这个质数的乘积是一个合法答案

对于每个使得\((p+1)(p-1)\lt S\)的\(p\),枚举可能的\(a_i\)进行递归

例四

一个\(n\times m\)的网格,\(k\)种颜色,部分格子已经涂了某种颜色,现在需要将其他格子也涂上颜色,使得从\((1,1)\)到\((n,m)\)的每条路径(每次向下或向右走一格)都不会出现重复颜色。求方案数,对\(10^9+7\)取模。

\(n,m\le 1000,k\le 10\)

解:

颜色数应大于等于步数,\(n+m-1\le k\Rightarrow n+m\le k+1\le 11\),否则puts("0")。
然后搜每个位置的颜色,可以状压到每个位置的已经过的颜色。
可行性剪枝:未经过的颜色数小于剩余步数,剪掉。
对称性剪枝:如果涂上一个未出现过的颜色,涂哪一个都是等价的,那么只需搜其中一个。

例五

有\(52\)张牌排成一排\(\{0,1\cdots 51\}\),然后可以把他们分成两半,然后交叉着洗牌(一个确定的置换)。
洗完牌后,可能会出现一个错误,将一对相邻的牌调换了位置,但是每次洗牌最多只会犯一个错误。
现在给出牌最终的顺序,求最少洗了多少次(保证不超过\(10\)),最少犯几次错误。

解:

一个性质:确定了洗牌次数后,若真实的最终排列与不犯错误的最终排列,有\(k\)个位置不同,那么犯错次数\(\lceil\frac{k}{2}\rceil\)。

以此来当做剪枝,枚举洗牌次数和犯错次数,跑\(idA^*\)

例六

你记录了\([0,59]\)这个时间段内到站的所有公交车(数量\(\le 300\)) 的时间,每辆车属于一条线路。

  • 同一路线路的车的到站时间间隔相同。
  • 每条线路在\([0,59]\)至少到达两辆车。
  • 最多有\(17\)条线路

求最少可能有多少条线路

解:

一条线路,可以通过第一辆、第二辆车来确定。
我们可以按照到站顺序来确定每辆车的归属情况。
如果它是线路第一辆车,那么先把他做一个标记。(同时,它的时间应在\([0,29]\))
如果是第二辆,枚举标记过的车辆来确定一条线路,如果可行,删除该线
路的所有车。
这样,随着搜索的深度增加,可选车辆快速减少,搜索规模大量降低。

同时可以加入若干合法性或者最优性剪枝。比如:作为第一辆的车多于未确定的车。

例七

给定\(n\)个字符集为\(\{a,b,c,\cdots,m\}\)的字符串\(S_i\),求一种\(\{a,b,c,\cdots,m\}\)到\(\{0,1,2,3,4,5,6,7,8,9,+,\times,=\}\)的映射\(f\),使得所有\(f(S_i)\)均是表达式合法、且成立的等式。

\(n\le 1000,5\le |S_i|\le 11\)

解:

先对约束条件较高的\(+,\times ,=\)进行搜索。
然后,对于每一个等式,我们得到了每个数的位数,计算等号两侧的值域,
无交集就可以剪枝。随着对应关系的确定,值域也会越来越小。
可以选择从低位开始搜,确定了低位的值,不相等可以剪枝

原文地址:https://www.cnblogs.com/kuaileyongheng/p/9743527.html

时间: 2024-10-08 14:26:25

雅礼学习10.3的相关文章

雅礼学习10.2

雅礼学习10.2 上午考试解题报告 各题状况(爆零) T1 想了10多分钟,暴力只有一个极大复杂度的想法,显然不可过,但还是写了,然后就全TLE也是...意料之中 T2 暴力很好写,但是错误理解了Tim给的部分分的意思:先给了一个\(a_i\le 10^9\),然后部分分里面没有提到\(a_i\)的情况,我就忽略了\(a_i\)的大小对答案统计的影响... 换句话说,我当时要是写了离散化,就是\(43\)分到手. T3 题目要求的输出可以分成三个问题,第一个问题正确 的话可以得到这个点的\(25

雅礼学习10.4

雅礼学习10.4 上午考试 各题状况 T1 莫名其妙20分了. 考场上一眼秒掉了这个题目:这不就是个并查集捆绑+快速幂么 然后开开心心这个点如果有这个质因子的话\(fa\)就指向这个质因子,而每个数字有多个质因子... 多个质因子在相互指\(fa\)的时候指乱套了.... 对拍的时候看出来的,然后用\(1\)个多小时来调这份代码,最后自己都不知道这东西在干嘛了,就凉了. T2 写了个暴力枚举,期望\(20\)实际\(20\) T3 看到成绩之后:这题怎么会爆\(long long\)的??? 然

雅礼学习10.5

雅礼学习10.5 上午考试 各题状况 T1 模拟挂成\(10\)分?? 每次更新答案的时候位置搞错了. 想到了可能是线段树动态开点,但没写出来,因为标记下传不会... T2 理解错了题目含义. 选出的\(m\)个物品中,至少要有\(k\)个是\(A\)喜欢的,至少\(k\)个是\(B\)喜欢的 那么很显然只要满足了上面的限制条件,俩人都不喜欢的也能选... 但考场上没想到这层 就凉了 正解变骗分,\(15\)分 T3 搞完上面两个题目之后没剩多少时间,就随便扔了个东西上去.. 也不知道写的是个啥

雅礼学习10.7

雅礼学习10.7 上午考试 各题状况 全TM神仙题... \(T1\) \(35\)分暴力 \(T2\) 我\(n=1\)的时候直接输出了\(1\),连这个格子上是否有方块都没判,当时是感觉...难道这个部分分也会卡么 结果出题人就这么卡了..就一分都没有了 太毒瘤了. \(T3\) 成功骗\(8\)分 做了一段时间之后去做牛客网的来着. 跟人要了份暴力 然后我TM..从紫名变成灰名了???? 题目及考场代码 T1 /* * 暴力思路:从初始位置开始进行bfs */ #include<queue

雅礼学习10.6

雅礼学习10.6 上午考试 各题状况 T1 二分答案 应该有反例,就是说,答案应该不是单调的 但是不会写其他的算法了啊... T2 我TM... 第二个红框圈出来的部分应该是 if(x1+x1!=s) 写错了,就没了\(18\)分.. T3 写了个\(n^4\)的暴力 最后发现题目中的矩形的四个顶点不一定是给定的顶点.. 那就GG了 各题题目及考场代码 T1 /* * 二分答案.. * 复杂度O(20(N+NlogN+M))的,感觉很悬 * 排序应该可以优化掉,但是不太会哎. */ #inclu

#6030. 【雅礼集训 2017 Day1】矩阵

#6030. 「雅礼集训 2017 Day1」矩阵 题目描述 有一个 n×n  的矩阵,每个位置 (i,j) 如果是 . 表示为白色,如果是 # 表示为黑色. 初始时,每个位置可以是黑色或白色的,(i,j)  位置的值会作为 ai,j 给你. 现在有一种操作,选择两个整数 i,j∈[1,n],记 (i,1),(i,2),…,(i,n) (i, 1), (i, 2)的颜色为 C1,C2,…Cn ??,将 (1,j),(2,j),…,(n,j)  的颜色赋为 C1,C2,…,Cn ??. 你的任务是

2017雅礼省选集训做题记录

嘛,最近在补雅礼省选前集训的题.都是我会做的题..那一定是最水的那些题啦 题目在loj.ac上都有.过段时间如果搬了雅礼NOI集训的题应该也会做做的吧.. Day1 T1 一道经典套路题,做法跟UOJ #228基础数据结构练习题类似. 使用线段树维护.考虑相邻两个数的差值最多变化log次.也就是说,对于每个区间,只要操作二进行大概log次就能使得这个区间内所有数完全一样.所以对于操作二,只要记录一下区间最大最小值,就能直接打标记或者暴力DFS下去. 和UOJ那个题一样,注意一个特殊情况,就是一个

2018雅礼 折射

雅礼题好难啊. 这个DP题思路好强. 这个东西首先一眼就知道按y排的DP怎么写,大概就是设$f(i,j,k)$表示考虑到y坐标从大到小排名为i的点,这线上一次转是j,上上次转是k的数量,直接二维限制转移就行了. 考虑这东西怎么优化. 前缀和能搞时间,woc空间也被卡了??? 打出来表看一看???? 这个DP数组有好多都是空的... 因为越往后x限制的越少. 然后我就不会了. 正经:设$f(i,0/1)$表示从x坐标排名为i,出门左转还是右转的线的数量. 我一开始就否掉了这东西因为好像转移顺序会出

c 函数及指针学习 10

标准库函数 1算数运算stdlib.h 2随机数stdlib.h 3字符串转化stdlib.h 4数学函数 math.h 5日期和时间 time.h 6信号 signal.h 7打印可变参数列表stdarg.h 8断言 assert.h 抽象数据类型在数据结构中比较仔细 运行时环境没看 来自为知笔记(Wiz)c 函数及指针学习 10,码迷,mamicode.com