The 16th Heilongjiang Provincial Collegiate Programming Contest / gym103107


 

Problem - A - Codeforces

And RMQ

吉司机线段树

假设对于要让区间[l,r]对于x做与操作
对于区间[l,r]维护他们的区间或(用qh表示)
分三种情况讨论
1.当qh|x=qh时,显然这一次修改不会造成影响,直接退出
2.当qh|x!=qh 是递归左右子树,直到l=r

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5e5+10;
struct node{
    int l,r,qh,ma;
}tr[N<<2];

int a[N];
 inline int rd()
{
    int x=0,f=1;char ch=getchar();
    while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
    while (isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

void pushup(int u){
    tr[u].ma=max(tr[u<<1|1].ma,tr[u<<1].ma);
    tr[u].qh=(tr[u<<1|1].qh|tr[u<<1].qh);
}

void build(int u,int l,int r){
    if(l==r) tr[u]={l,r,a[l],a[l]};
    else{
        tr[u]={l,r};
        int mid=l+r>>1;
        build(u<<1,l,mid);
        build(u<<1|1,mid+1,r);
        pushup(u);
    }
}


void update(int u,int pos,int w){
    if(tr[u].l==pos&& tr[u].r==pos) {
            tr[u].ma=w;
            tr[u].qh=w;
    }
    else{
        int mid = tr[u].l + tr[u].r >> 1;
        if(pos <= mid) update(u << 1,pos,w);
        else update(u << 1|1,pos,w);
        pushup(u);
    }
}

int query(int u,int l,int r){
    if(tr[u].l > r || tr[u].r < l) return 0;
    if(tr[u].l >= l && tr[u].r <= r) return tr[u].ma;
    int mid = tr[u].l + tr[u].r >> 1;
    return max(query(u << 1,l,r),query(u << 1|1,l,r));
}
void modify(int u,int l,int r,int x){
//    cout<<tr[u].l<<"_"<<tr[u].r<<endl
//    if(l==0||r==0)return;
    if(tr[u].l==tr[u].r){
        tr[u].qh&=x;
        tr[u].ma&=x;
        return;
    }
    if((x&tr[u].qh)==tr[u].qh)return;
//    cout<<">";
    int mid=tr[u].l+tr[u].r>>1;
    if(l<=mid)modify(u<<1,l,r,x);
    if(r>mid)modify(u<<1|1,l,r,x);
    pushup(u);
}
int main(){
    int n=rd(),m=rd();
    for(int i=1;i<=n;i++){
        a[i]=rd();
    }
    build(1,1,n);
    while(m--){
        char opt[10];
        int l,r,x;
        scanf("%s",opt);
        if(opt[0]=='A'){
//            cout<<"??";
            l=rd(),r=rd(),x=rd();
            modify(1,l,r,x);
        }
        else if(opt[0]=='U'){
            l=rd(),x=rd();
            update(1,l,x);
        }
        else {
            l=rd(),r=rd();
            printf("%d\n",query(1,l,r));
        }
    }
}

 

 

 Problem - D - Codeforces

Doin' Time

区间dp板子题

定义dp[i][j]为合并区间i到j获取的分数,w[i][j]为区间i到j内的树相乘取模100003的值

转移方程:dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]+(w[i][k]-w[k+1][j])*(w[i][k]-w[k+1][j]));

 

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1000003;
int n,a[400];
ll dp[400][400],w[400][400];

int main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    for(int i=1;i<=n;i++){
        ll tmp=1;
        for(int j=i;j<=n;j++){
            tmp=(tmp*a[j])%mod;
            w[i][j]=tmp;
//            cout<<i<<" "<<j<<" "<<tmp<<endl;
        }
    }
    for(int len=2;len<=n;len++){
        for(int i=1;i+len-1<=n;i++){
            int j=i+len-1;
            for(int k=i;k<j;k++){
                dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]+(w[i][k]-w[k+1][j])*(w[i][k]-w[k+1][j]));
            }
        }
    }
    cout<<dp[1][n]<<endl;
}

 

Problem - E - Codeforce

 Elastic Search

 

将所有串插入trie树并建立ac自动机。 [公式] 表示u结点表示的字符串出现的数量。 [公式] 表示u在trie树上的父节点。 [公式] 表示u在fail树上的父结点。之后考虑用 [公式] 表示以结点u对应的字符串为起点的最长字符串嵌套的长度。

可以发现 [公式] 。直接遍历trie树转移即可。时间复杂度 [公式] 。建立fail树后原来的tire树会发生改变,所以需要复制一个trie2  在遍历trie2树时进行状态转移

 

 

 

#include<bits/stdc++.h>
using namespace std;
const int N=500010;
int fail[N],fa[N],tree[N][26],wend[N],cnt=0;
int tree2[N][26];
int dp[N],vis[N],ans=0;
inline void build(string s){
    int now=0;
    for(int i=0;i<s.size();i++){
        int next=s[i]-'a';
        if(!tree[now][next])
            tree[now][next]=++cnt,tree2[now][next]=tree[now][next];
        now=tree[now][next];
    }
    wend[now]++;
}

void get_fail(){
    queue<int>q;
    for(int i=0;i<26;i++){
        if(tree[0][i]){
            fail[tree[0][i]]=0;
            q.push(tree[0][i]);
        }
    }
    while(!q.empty()){
        int u=q.front();
        q.pop();
        for(int i=0;i<26;i++){
            if(tree[u][i]){
                fail[tree[u][i]]=tree[fail[u]][i];
                q.push(tree[u][i]);
            }
            else{
                tree[u][i]=tree[fail[u]][i];
//                cout<<tree[u][i]<<" "<<u<<"_"<<i<<" fail[u]"<<fail[u]<<endl;
            } 
        }
    }    
}

void bfs(){
    queue<int>q;
    q.push(0);
    dp[0]=0;
    while(!q.empty()){
        int u=q.front();
//        vis[u]=1;
        q.pop();
        for(int i=0;i<26;i++){
            if(tree2[u][i]){
                dp[tree2[u][i]]=max(dp[u],dp[fail[tree2[u][i]]])+wend[tree2[u][i]];
                ans=max(ans,dp[tree2[u][i]]);
                q.push(tree2[u][i]);
            }
        }
    }
}

int main(){
    int n;
    cin>>n;
    string s;
    for(int i=1;i<=n;i++){
        cin>>s;
        build(s);
    }
    fail[0]=0;
    get_fail();
//    for(int i=1;i<=9;i++)cout<<i<<"_"<<fa[i]<<endl;
    bfs();
    cout<<ans<<endl;
}

 

problem-F-codeforce

Function

欧拉筛板子题

 对输入的n跑一遍欧拉筛,主要是求不同数的贡献和。如果是1或者素数res+=1,给你的公式3是质因数分解后对第一个质因数及其指数的函数值乘上这个数的其他质因数(包含其指数)。在实现的时候我们发现也可以将公式2、3合并,即tmp值为1或者其他数。

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
#define ll long long
#define int long long
const int maxn = 1e7 + 20;
const int inf = 0x3f3f3f3f;
const int Base = 131;
const ll INF = 1ll << 62;
// const double PI = acos(-1);
const double eps = 1e-7;
const int mod = 1e9 + 7;
#define mem(a, b) memset(a, b, sizeof(a))
#define speed                        \
    {                                \
        ios::sync_with_stdio(false); \
        cin.tie(0);                  \
        cout.tie(0);                 \
    }

inline ll gcd(ll a, ll b)
{
    return b == 0 ? a : gcd(b, a % b);
}

inline int rd()
{
    int x;
    scanf("%lld", &x);
    return x;
}

void ex_gcd(int a, int b, int &x, int &y)
{
    // cout <<" a = " << a <<" b = " << b <<" x = " << x <<" y = " << y << endl;  
    if (b == 0){
        x = 1;
        y = 0;
        return;
    }
    ex_gcd(b, a % b, x, y);
    int tmp = x;
    x = y;
    y = tmp - (a / b) * y;
}

int primes[maxn], vis[maxn] = {0}, cnt = 0;

// 欧拉筛板子修改
int Euler(int n){
    int res = 1;
    for(int i = 2; i <= n; i++){
        // 如果是素数
        if(!vis[i]){
            vis[i]=1;
            primes[cnt++] = i;
            res++;
        }
        for(int j = 0; j < cnt && primes[j] <= n / i; j++){
            int tmp = primes[j] * i;
            vis[tmp] = 1;
            int count = 0;
            // 获取指数大小
            while(tmp % primes[j] == 0){
                count++;
                tmp /= primes[j];
            }
            // 通过打表找规律可以发现2、3可以合并写
            res += pow(primes[j], count / 2) * tmp;
            if(i % primes[j] == 0){
                break;
            }
        }                        
    }
    return res;
}

signed main(){
    int n = rd();
    printf("%lld\n", Euler(n));
    system("pause");
    return 0;
}

Problem - G - Codeforces

Go? No

思路:使用tarjan计算每个连通块放子之后能产生的最大连通块数量,最大连通块数量等于连接一个割点的桥的数量。注意特判连通块大小为1的时候的情况。

删除一个无向图中的点,能使得原图增加几个连通分量?
如果该点是一个孤立的点,那么增加-1个。
如果该点不是割点,那么增加0个。
如果该点是割点且非根节点,那么增加该点在dfs树中(无反向边连回早期祖先的)的儿子数。
如果该点是割点且是一个dfs树的根节点,那么增加该点在dfs树中(无反向边连回早期祖先的)的儿子数-1的数目,也就是增加了以该dfs树的儿子数目-1

 

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int>pii;
const int N=4300000;
int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
char mp[2010][2010];
int id[2010][2010];
int n,m,ecnt,num,root,top;
bool iscut[N];
struct edge{
    int v,next;
}e[4*N];
int head[N],dfn[N],low[N],cut[N],stack[N],dclock,ins[N];


void init(){
    memset(head,-1,sizeof head);
    ecnt=0;
}
void addedge(int u,int v){
    e[ecnt]={v,head[u]};
    head[u]=ecnt++;
}

void tarjan(int x){
    if(x==root)cut[x]--;
    dfn[x]=low[x]=++dclock;
    int child=0;
    for(int i=head[x];~i;i=e[i].next){
        int v=e[i].v;
        if(!dfn[v]){
            child++;
            tarjan(v);
            low[x]=min(low[x],low[v]);
            if(low[v]>=dfn[x]){
                cut[x]++;
                if(x!=root||child>1)iscut[x]=1;
            }
        }
        else low[x]=min(low[x],dfn[v]);
    }
}

int main(){
    init();
    cin>>n>>m;    
    for(int i=1;i<=n;i++){
        scanf("%s",mp[i]+1);
    }
    int cnt=0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(mp[i][j]=='.')id[i][j]=++cnt;
        }
    }
    int flag=0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(mp[i][j]=='.'&&mp[i][j+1]=='.'){
                addedge(id[i][j],id[i][j+1]);
                addedge(id[i][j+1],id[i][j]);
            }
            if(mp[i][j]=='.'&&mp[i+1][j]=='.'){
                addedge(id[i][j],id[i+1][j]);
                addedge(id[i+1][j],id[i][j]);
            }
        }
    }
    int k=0;
    for(int i=1;i<=cnt;i++){
        if(!dfn[i]){
            k++;
            root=i;
            tarjan(i);
        }
    }
    if(k==cnt){
        cout<<k-1<<endl;
        return 0;
    }
    int res=0;
    for (int i=1; i<=cnt; i++) 
        if(iscut[i])
            res=max(res,cut[i]);
    cout<<k+res<<endl;

}

 

 

 problem-H-hack DSU!

Hack DSU!

水题

求怎样给样例才会让题目中给的并查集代码运行次数超过T,即运行次数最大

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

int rd(){
    int x;
    scanf("%d", &x);
    return x;
}

vector<int>v;

int main(){
    
    int n = rd();
    int m = rd();
    for(int i = n; i >= 2; i--){
        printf("%d %d\n", i, i - 1);
    }    
    printf("%d %d\n", 1, n);
//    system("pause");
    return 0;
}

Problem - I - Codeforces

ICU4C

博弈论:NIM阶梯游戏+SG函数,对于一条赛道,连在一起的n个小车可以任选右边(1——n)辆向右移动一个单位,相当于阶梯nim游戏的一层阶梯上可以移动(1——n)个小球到下一层

每一辆车,他右边空格的数量就是他所在的阶梯位置

那么这就是一个经典阶梯NIM游戏,求奇数层的小球数的异或和,多条赛道同时进行,直接套用SG函数

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int>pii;

inline int rd(){
    int x;
    scanf("%d",&x);
    return x;
}
int main(){
    int t=rd();
    while(t--){
        int n=rd();
        vector<int>mex;
        for(register int i=1;i<=n;i++){
            int m=rd(),e=rd();
            vector<int>x;
            for(register int j=1;j<=m;j++){
                int tmp=rd();
                x.push_back(tmp);
            }
            sort(x.begin(),x.end());
            vector<pii>pos;
            int now=0,base=0;
            for(register int j=0;j<x.size();j++){
                if(x[j]+1==x[j+1]){
                    now++;
                    continue;
                }
                int tmp=e-x[j]-(x.size()-j-1);
                if(tmp%2==1){
                    base^=(now+1);
                }
                now=0;
            }
            mex.push_back(base);
        }
        int base=0;
        for(auto tmp:mex){
            base^=tmp;
        }
        if(base==0){
            cout<<"Bob"<<endl;
        }
        else cout<<"Alice"<<endl;
    }
        
}

 

 

problem-J-JOJO

JOJO's Factory

水题

判断一下左右两边能够取的机器数量,取min就好了

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1000003;
const int maxn=5e5+10;
int n,m;
int mp[maxn],mp2[maxn];
int main(){
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;i++)mp[i]=0,mp2[i]=0;
    int a=n,b=n;
    for(int i=1;i<=m;i++){
        int c,d;
        scanf("%d %d",&c,&d);
        mp[c]++;
        mp2[d]++;
        if(mp[c]>=n)a--;
        if(mp2[d]>=n)b--;
    }
    printf("%d\n",min(a,b));
    return 0;
}

 K - Keep Eating

 水题

最优情况下最后留下的就是和能吃个数一样的情况,即留下最少的数量

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define int ll
const int M=2e5;
int t1[M],t2[M];
int n,m;
inline int rd()
{
    int x=0,f=1;char ch=getchar();
    while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
    while (isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}


signed main(){
    int n = rd();
    int m = rd();
    int res = 0;
    int sum = 0;
    for(int i = 0; i < n; i++){
        int x = rd();
        sum += x;
    }
    if(sum<m)cout<<"0"<<endl;
    else cout<<sum-(m+1)/2<<endl;
    return 0;
}

 Problem - L - Codeforces

Labi-Ribi

首先, [公式] 是没用的,只有 [公式] 有用

那么问题转换为, [公式] 个物品,当 [公式] 时才可拿,且拿后 [公式] ,问最低多少可以拿完全部。

这是一个经典之典中典问题

那么显然, [公式] 的无脑拿,贪心从限制低的到限制高的拿

那么现在考虑拿 [公式] 的情况,考虑先拿了i就不能拿j,而先拿了j反而可以拿i的情况

V+Ci<Hi

V+Cj>=Hj

Hi-Cj<=v<Hj-ci

Hi+Ci<=Hj+Cj

所以只需要按照Hi+Ci 的值倒序进行贪心就好

发现新加DLC只需要插入到对应位置,这个操作和贪心询问是可以同时进行的,因此可以 [公式] 完成

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+10;
int h[N];
inline int rd(){
    int x;
    scanf("%lld",&x);
    return x;
}

struct bz{
    int h,c;
};

struct bf{
    int h,c;
};
bool cmp1(bz a,bz b){
    return a.h<b.h;
}
bool cmp2(bf a,bf b){
    return a.h+a.c>b.h+b.c;
}
vector<bz>cz;
vector<bf>cf;
inline int getans(int h,int c){
    int res=-3e9-10,now=0,flag=0;
    for(register int i=0;i<cz.size();i++){
        if(cz[i].h>=h&&c>0){
            res=max(res,h-now);
            now+=c;
            bz tmp=cz[i];
            cz[i]={h,c};
            cz.push_back({0,0});
            for(register int j=i+1;j<cz.size();j++){
//                cout<<tmp.h<<endl;
                swap(tmp,cz[j]);
                res=max(res,cz[j].h-now);
                now+=cz[j].c;    
                flag=1;
            }    
            break;
        }
        res=max(res,cz[i].h-now);
        now+=cz[i].c;    
    }
    if(!flag&&c>0){
        cz.push_back({h,c});
        res=max(res,h-now);
        now+=c;
    }
    flag=0;
    for(register int i=0;i<cf.size();i++){
        if((cf[i].h+cf[i].c<=h+c)&&c<=0){
            bf tmp=cf[i];
            cf[i]={h,c};
            res=max(res,cf[i].h-now);
            now+=cf[i].c;
            cf.push_back({0,0});
            for(register int j=i+1;j<cf.size();j++){
                swap(tmp,cf[j]);
                res=max(res,cf[j].h-now);
                now+=cf[j].c;    
            }    
            flag=1;
            break;
        }
        res=max(res,cf[i].h-now);
        now+=cf[i].c;
    }
    if(!flag&&c<=0){
        cf.push_back({h,c});
        res=max(res,h-now);
        now+=c;
    }
    return res;
}
signed main(){
    int n=rd(),q;
    for(int i=1;i<=n;i++){
        h[i]=rd();
    }
    for(int i=1;i<=n;i++){
        int a=rd(),b=rd();
        int c=b-a;
        if(c>0)cz.push_back({h[i],c});
        else cf.push_back({h[i],c});
    }
    sort(cz.begin(),cz.end(),cmp1);
    sort(cf.begin(),cf.end(),cmp2);
    int res=-3e9-10,now=0;
    for(register auto x:cz){
        res=(res>x.h-now)?res:x.h-now;
        now+=x.c;    
    }
    for(register auto x:cf){
        res=(res>x.h-now)?res:x.h-now;
        now+=x.c;
    }
    printf("%lld\n",res);    
    q=rd();
    while(q--){
        int h=rd(),a=rd(),b=rd();
        int c=b-a;
        printf("%lld\n",getans(h,c));    
    }
}
//4
//2 4 5 6
//1 2
//1 2
//1 2
//1 2
//1
//3 1 2

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM