2021 HZ国庆集训DAY1笔记——杂题选讲1

话说这好像还是我第一次听hkk神仙讲课呢......

结果hkk把ppt标题打错了,害的一群人早上半天全在想DP......

热身题

T1:CCPC2021 网络赛 F

难度:D1T1

签到题,注意到的值为4,所以我们对n%4进行分类。代码如下:

 

#include<iostream>
using namespace std;
#define il inline
int read() {
    int s=0,w=1; char ch=getchar();
    while(ch<'0' || ch>'9') { if(ch=='-') w=-1; ch=getchar();}
    while(ch>='0' && ch<='9') s=s*10+ch-'0',ch=getchar();
    return s*w;
} 
int t,n;
void solve(int n) {
    int k=n%4;
    if(k==0) printf("%d\n",n);
    if(k==1) printf("%d\n1",n);
    if(k==2) printf("%d\n0001",n+2);
    if(k==3) printf("%d\n01",n-1);
    for(int i=1;i*4<=n;i++) printf("1001");
    printf("\n");
    return;
}
int main() {
//    freopen()
    t=read();
    while(t) {
        t--;
        n=read();
        solve(n);
    }
    return 0;
}

 

T2:CCPC2021 网络赛 G

难度:D1T2

因为g(999999)=54,所以对于任意n<=1e6,都有g(n)<=54。所以对于每一组(A,B,C,D),我们可以穷举g(x)的取值,这样我们就可以得到一个关于x的二次函数。提前预处理出g(x)的每个值对应的x的取值,根据二次项前的系数求出距对称轴最远/近的点,即为答案。

代码:待补(×)

 

T3:原题: CCPC2021 网络赛 K

难度:D1T2

不算很难想的DP,但细节真的很多。以列数作为阶段,用f(i,j)表示打到第i列,用掉j发子弹的最优值。提前预处理出每一列用掉j发子弹时的最优值。此时注意,因为可能打完最后一枪后又奖励了一发子弹,这两种情况对后续状态的贡献是不一样的。所以我们需要预处理两个东西,一是最后一发子弹打在当列时的最优值,二是最后一发子弹被奖励回来时的最优值。同理,dp数组也应该开两个。

代码(有点小问题等我有空再改):

#include<bits/stdc++.h>
using namespace std;
#define il inline
int read() {
    int s=0,w=1; char ch=getchar();
    while(ch<'0' || ch>'9') { if(ch=='-') w=-1; ch=getchar();}
    while(ch>='0' && ch<='9') s=s*10+ch-'0',ch=getchar();
    return s*w;
} 
int n,m,k,t;
int a[202][202],is[202][202],fn[202][202],fy[202][202],dpn[202][202],dpy[202][202],ans;
void init() {
    memset(fn,0,sizeof(fn));
    memset(dpn,0,sizeof(dpn));
    memset(fy,0,sizeof(fy));
    memset(dpy,0,sizeof(dpy));
    ans=0;
    for(int i=1;i<=n;i++) {
        for(int j=1;j<=m;j++) {
            char ch;
            scanf("%d %c",&a[i][j],&ch);         
            if(ch=='Y') is[i][j]=1;            
        }
    }
}
void work() {
    for(int i=1;i<=m;i++) {
        int cnt=0;
        for(int j=n;j>=1;j--) {
            if(is[i][j]) fy[i][cnt]+=a[j][i];        
            else {
                cnt++;
                fy[i][cnt]=fy[i][cnt-1]+a[j][i];      
                fn[i][cnt]=fy[i][cnt-1]+a[j][i];         
            }              
        }
    }
    
    for(int i=1;i<=m;i++) { 
        for(int j=0;j<=k;j++) { 
            for(int r=0;r<=j && r<=n;r++) { 
                dpy[i][j]=max(dpy[i][j],dpy[i-1][j-r]+fy[i][r]);       
                if(r) dpn[i][j]=max(dpn[i][j],dpn[i-1][j-r]+fn[i][r]);  
                if(j-r) dpn[i][j]=max(dpn[i][j],dpn[i-1][j-r]+fy[i][r]); 
            } 
        } 
    } 
    
} 
int main() {
//    t=read();
//    while(t--) {
        n=read(); m=read(); k=read();  
        init();              
        work();         
        printf("%d\n",max(dpy[m][k],dpn[m][k]));  
//    }
    return 0;
}

 

T4: ICPC2021 网络赛Ⅱ K

难度:D2T2

(你管这叫热身?)这个n的规模一眼状压。设s为已经取走的菜的集合,f(i,s,j)表示第i个人在已选菜集合为s时选择j菜的概率,对于每个状态,我们直接枚举当前能选择的菜,用刷表法更新(有点像前两天写的宝藏)

代码:待补 

 

正式题

(渐渐开始毒瘤)

T1: CCPC2021 网络赛 L

难度:D2T1

f(n)删掉n个石子时的最小步数。我们通过打(cai)表(ce)可知,f(n)非严格单调递增。(具体证明可参考 这篇博客)这样我们就有了一个显然的贪心思想,每次优先选择选择n mod p 最大的方案,则有f(n)=f(n-(n mod p))+1。现在的问题在于,如何找到这样的p。在n+1不是p的倍数的情况下,n+1 mod p的值即为(n mod p)+1。所以我们使用链表来存储这些质数。当n++时,我们对n的质因子采取暴力修改接到链表尾部,这样每次直接取出链表头部求出f(n)即可。

代码:

#include<bits/stdc++.h>
using namespace std;
#define il inline
#define ll long long
//debug
void dg() { printf("114514 ");}
void print(int x) { printf("%d ",x);}
//
il int read() {
    int s=0,w=1; char ch=getchar();
    while(ch<'0' || ch>'9') { if(ch=='-') w=-1; ch=getchar();}
    while(ch<='9' &&  ch>='0') s=s*10+ch-'0',ch=getchar();
    return s*w;
}
const int N=2e6+5;
const int M=1e5+5;
int t,n,m,vis[N],f[N];
vector<int> num[N];
void prework() {
    for(int i=2;i<=2e6;i++) {
        if(vis[i]) continue;
        num[i].push_back(i);
        for(int j=i;j<=2e6/i;j++) {
            vis[i*j]=1;  
            num[i*j].push_back(i);
            if(i*j>N) dg();
        }
    }
    f[1]=1;
    return;
}
struct node{
    int prev,nex,val;
} p[M]; int head,tail;
bool cmp(node a,node b) {
    return a.val<b.val;
}
void change(int k) {
    p[p[k].prev].nex=p[k].nex;
    p[p[k].nex].prev=p[k].prev;//删掉 
    
    p[k].prev=p[tail].prev;
    p[k].nex=tail; //更改k位置指针 
    
    p[p[tail].prev].nex=k;
    p[tail].prev=k; //将k插入末尾 
}
void deal() {
    memset(vis,0,sizeof(vis));
    sort(p+1,p+m+1,cmp);
    for(int i=1;i<=m;i++) {
        p[i].prev=i-1; p[i].nex=i+1; vis[p[i].val]=i;
    }
    head=0; tail=m+1;
    p[head].nex=1; p[tail].prev=m;
    for(int i=2;i<=n;i++) {
        for(int j=0;j<num[i].size();j++) if(vis[num[i][j]]) change(vis[num[i][j]]);          
        f[i]=f[i-(i%p[p[head].nex].val)]+1;                             
    }
    return;
}
int main() {
    t=read();
    prework();
    while(t) {
        t--;
        n=read(); m=read();
        for(int i=1;i<=m;i++) p[i].val=read();
        deal(); 
        for(int i=1;i<=n;i++) printf("%d ",f[i]);  
        printf("\n"); 
    }
    return 0;
}

 

 

 

 

T2:CCPC2021 网络赛 H

难度:D1T3

不考虑存在更大的公因数的情况下,若v(l,r)=x,则区间(l,r)中至少含有两个x的倍数。“至少”这种东西数据结构很难维护,我们考虑它的互补问题,求只含有1个或不含有x的倍数的区间数。

我们从大到小枚举x的值,维护对于每一个r,我们用线段树维护使v(l,r)<x最小l。具体实现......貌似要用到Segmenttreebeats。暂时不会,先空着。

代码:待补

 

T3:ICPC2021 网络赛 J

 没听懂,先空着

 

T4:ICPC2021 网络赛 E

难度:D2T1

线段树维护dfs序的板子,没啥好说的

代码:略

 

T5:ICPC2021 网络赛 II H

难度:D1T2

愚人节题?构造方法:每个集合随机塞 512/r数,可以证明不满足题意的可能性小到可以忽略不计。

代码:略

 

T6:ICPC2021 网络赛 II L

毒瘤数据结构,不会,跳过

 

T6:CF1580C

不会根号分治,跳过

 

T7:CF1580D

神仙DP,不会,跳过

 

附加题:CF1580E

推式子,最后的结果是,会发现这其实是树上两点的距离公式,所以只需要构造出一棵任意两节点的lca是两点路径上的最小值的树就行了。这是啥?笛卡尔树。

代码:待补

 

 

 

posted @ 2021-10-05 20:09  残碑小筑  阅读(56)  评论(0)    收藏  举报
faults = { minSize : 10, maxSize : 20, newOn : 1000, flakeColor : "#FFFFFF" /* 此处可以定义雪花颜色,若要白色可以改为#FFFFFF */ }, options = $.extend({}, defaults, options); var interval= setInterval( function(){ var startPositionLeft = Math.random() * documentWidth - 100, startOpacity = 0.5 + Math.random(), sizeFlake = options.minSize + Math.random() * options.maxSize, endPositionTop = documentHeight - 200, endPositionLeft = startPositionLeft - 500 + Math.random() * 500, durationFall = documentHeight * 10 + Math.random() * 5000; $flake.clone().appendTo('body').css({ left: startPositionLeft, opacity: startOpacity, 'font-size': sizeFlake, color: options.flakeColor }).animate({ top: endPositionTop, left: endPositionLeft, opacity: 0.2 },durationFall,'linear',function(){ $(this).remove() }); }, options.newOn); }; })(jQuery); $(function(){ $.fn.snow({ minSize: 5, /* 定义雪花最小尺寸 */ maxSize: 80,/* 定义雪花最大尺寸 */ newOn: 200 /* 定义密集程度,数字越小越密集 */ }); });