Educational Codeforces Round 58 (Rated for Div. 2)

A. Minimum Integer

#include<bits/stdc++.h>
#define clr(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll; 
const int maxn=1e3+10;
struct node{
    ll num,mag;
    friend bool operator <(const node &a,const node &b){
        return a.mag>b.mag;
    }
}a[maxn];
int n;
ll p[70];
ll add(ll x,ll val){
    for(int i=60;i>=0;i--)
    {
        if(x&(1<<i))
        {
            if(!p[i]){
                p[i]=x;
                break;
            }else{
                x=x^p[i];
            }
        }
    }
    if(x>0)return val;
    return 0;
}
int main(){
    while(cin>>n)
    {
        ll l,r,d;
        while(n--)
        {
            cin>>l>>r>>d;
            if(d<l)printf("%lld
",d);
            else{
                printf("%lld
",(r/d+1)*d);
                
            }
        }
    }
}
View Code

B. Accordion

小模拟,从前往后扫到第一个 [: ,从后往前扫到第一个 :] ,然后数中间的 “|” 就可以了。

#include<bits/stdc++.h>
#define clr(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll; 
const int maxn=5e5+10;
char s[maxn];
int main(){
    while(scanf("%s",s+1)!=EOF)
    {
        int flag=0,p1=-1,p2=-1,len=strlen(s+1);
        for(int i=1;i<=len;i++)
        {
            if(flag==0)
            {
                if(s[i]=='[')flag=1;
            }else{
                if(s[i]==':'){
                    p1=i;
                    break;
                }
            }
        }
        flag=0;
        for(int i=len;i>p1&&i>0;i--)
        {
            if(flag==0)
            {
                if(s[i]==']')flag=1;
            }else{
                if(s[i]==':'){
                    p2=i;
                    break;
                }
            }
        }
        if(p1==-1||p2==-1){
            printf("-1
");
            continue;
        }
        int tot=0;
        for(int i=p1+1;i<p2;i++)
        {
            if(s[i]=='|')tot++;
        }
        printf("%d
",tot+4);
    }
}
View Code

C. Division and Union

题意:把给出的区间分成两部分,使不同部分的区间,不会有交点。

按 l 排序一遍,如果 l 小于前面最大的 r ,则必须和前面分在同一个群,否则就到第二个群。

#include<bits/stdc++.h>
#define clr(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll; 
const int maxn=2e5+10;
int T,n;
struct node{
    int l,r;
    int id;
    friend bool operator <(const node &a,const node &b)
    {
        return a.l<b.l;
    }
}a[maxn]; 
int ans[maxn];
int main(){
    cin>>T;
    while(T--)
    {
        cin>>n;
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d",&a[i].l,&a[i].r);
            a[i].id=i;
        }
        sort(a+1,a+1+n);
        int maxx=a[1].r,tep=0,flag=0;
        ans[a[1].id]=1;
        for(int i=2;i<=n;i++)
        {
            if(a[i].l<=maxx){
                ans[a[i].id]=1+tep;
            }else{
                ans[a[i].id]=1+1-tep;
                tep=1-tep;
                flag=1;
            }
            maxx=max(maxx,a[i].r);
            
        }
        if(flag==0)puts("-1");
        else{
            for(int i=1;i<=n;i++)
            {
                printf("%d%c",ans[i]," 
"[i==n]);
            }
        }
    }
}
View Code

D. GCD Counting

题意:给出一颗点带权的数,求任意两点简单路径上的点gcd大于1的长度最大是多少。

题解:

     gcd大于1即不互质,若一条路径是合法路径,则必定有一个大于1的公共质因子。所以我们可以对每个小于2e5的质数重新建树,然后再求树的直径。

       建图是怎么建的呢,其实只要把边读入的时候,就把边塞到各自的质因子背包里面去,最后把每个背包的边都建立出来,跑树上直径。

  但是有一个问题是,小于2e5的质数有一万多个,树的直径又是O(n)的,这样看上去会超时,我也困扰了好久。其实,两个点之间的公共gcd的质因子个数最多就十个左右,(再多就爆了)也就意味着每个点最多被建图十次,所以如果跑树的直径最后的总计算次数也就是10*O(n)左右,不会超时,这类问题以后要注意,明明很快就想到标算的,但迟迟不敢动手。

#include<bits/stdc++.h>
#define clr(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll; 
const int maxn=2e5+10;
int prim[maxn],n,u,v,a[maxn];
int gcd(int a,int b){
    return b==0?a:gcd(b,a%b);
}
void initPrim(){
    for(int i=2;i<=2e5/2;i++)
    {
        for(int j=2;i*j<=2e5;j++)
        {
            prim[i*j]=1;
        }
    }
}
struct edge{
    int u,v;
};
vector<edge>ve[maxn];
vector<int >e[maxn];
vector<int >jont;
bool vis[maxn];
int ans=0;
int dfs(int u,int fa,int dep){
    vis[u]=1;
    int f=0;
    int flink=0,slink=0;
    for(int i=0;i<e[u].size();i++)
    {
        int v=e[u][i];
        if(v==fa)continue;
        f=1;
        int tep=dfs(v,u,dep+1);
        if(tep>flink)slink=flink,flink=tep;
        else if(tep>slink)slink=tep;
    }
    ans=max(ans,max(dep+flink,flink+1+slink));
    return flink+1;
    
}
int main(){
    initPrim();
    while(cin>>n)
    {
        ans=1;
        int fff=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            if(a[i]>1)fff=1;
        }
        for(int i=2;i<=2e5;i++)ve[i].clear();
        for(int i=1;i<n;i++)
        {
            scanf("%d%d",&u,&v);
            int k=gcd(a[u],a[v]);
            if(k>1){
                for(int j=1;j*j<=k;j++)
                {
                    if(k%j==0)
                    {
                        if(j!=1&&prim[j]==0)    ve[j].push_back({u,v});
                        if(j*j!=k&&prim[k/j]==0) ve[k/j].push_back({u,v});
                    }
                }
            }
        }
        if(fff==0){
            puts("0");
            continue;
        }
        clr(vis,1);
        for(int i=2;i<=2e5;i++)
        {
            if(!prim[i])
            {
                for(int j=0;j<ve[i].size();j++)
                {
                    if(vis[ve[i][j].u]==1)
                    {
                        jont.push_back(ve[i][j].u);
                    }
                    if(vis[ve[i][j].v]==1)
                    {
                        jont.push_back(ve[i][j].v);
                    }
                    vis[ve[i][j].u]=0;
                    vis[ve[i][j].v]=0;
                    e[ve[i][j].u].push_back(ve[i][j].v);
                    e[ve[i][j].v].push_back(ve[i][j].u);
                }
                for(int j=0;j<jont.size();j++)
                {
                    vis[jont[j]]=0;
                }
                for(int j=0;j<jont.size();j++)
                {
                    int u=jont[j];
                    if(!vis[u]){
                        dfs(u,0,1);
                    }
                }
                for(int j=0;j<jont.size();j++)
                {
                    e[jont[j]].clear();
                }
                jont.clear();
            }
        }
        cout<<ans<<endl;
    }
}
View Code

E. Polycarp's New Job

为什么这会是E题,一开始我以为是求有几张钞票能被塞进去,看了样例才发现我想太多了。

反正就钞票放进去的时候,固定格式,短的一边在下面,钱包也是这样,分别记录长边和短边的最大值,比一下即可。

#include<bits/stdc++.h>
#define clr(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll; 
const int maxn=2e5+10;
int T,n;
struct node{
    int l,r;
    int id;
    friend bool operator <(const node &a,const node &b)
    {
        return a.l<b.l;
    }
}a[maxn]; 
int ans[maxn];
int main(){
    cin>>T;
    int maxl=0,maxr=0,l,r;
    char op[3];
    while(T--)
    {
        scanf("%s%d%d",op,&l,&r);
        if(op[0]=='+'){
            if(l>r)swap(l,r);
            maxl=max(maxl,l),maxr=max(maxr,r);
        }else{
            if(l>r)swap(l,r);
            if(l>=maxl&&r>=maxr)printf("YES
");
            else puts("NO");
        }
    }
}
View Code

F. Trucks and Cities

好题,题解移步我的另外一篇博客  点这里

#include<bits/stdc++.h>
#define clr(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll; 
const int maxn=402;
ll ans;
int dp[maxn][maxn][maxn],a[maxn];
int n,m;
int main(){
    while(cin>>n>>m)
    {
        ans=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=i;j<=n;j++)
            {
                dp[i][j][0]=a[j]-a[i];
            }
        }
        for(int k=1;k<=n;k++)
        {
            for(int i=1;i<=n;i++)
            {
                int w=i;
                for(int j=i;j<=n;j++)
                {
                    while(w<j&&max(dp[i][w][k-1],a[j]-a[w])>max(dp[i][w+1][k-1],a[j]-a[w+1]))w++;
                    dp[i][j][k]=max(dp[i][w][k-1],a[j]-a[w]);
                }
            }
        }
        int s,f,r;
        ll c;
        while(m--)
        {
            scanf("%d%d%lld%d",&s,&f,&c,&r);
            ans=max(ans,dp[s][f][r]*c);
        }
        cout<<ans<<endl;
    }
} 
View Code

G. (Zero XOR Subset)-less

题意:给出一个序列,试将其划分为尽可能多的非空子段,满足每一个元素出现且仅出现在其中一个子段中,且在这些子段中任取若干子段,它们包含的所有数的异或和不能为0.

思路:先处理出前缀异或,这样选择更多的区间其实就相当于选择更多的前缀异或,并且这些前缀异或不能异或出0,这就变成了线性基的基础题了。贪心的放,能放就放。不能放就意味着线性基的add函数里面的val最后变成了0,也就是当前已经插入的线性基已经可以异或出正在插入的数了,所以不能放。

#include<bits/stdc++.h>
#define clr(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll; 
const int maxn=2e5+10;
ll a[maxn],p[40],s[maxn];
int n;
int add(ll val){
    for(int i=30;i>=0;i--)
    {
        if(val&(1<<i)){
            if(!p[i]){
                p[i]=val;
                return 1;
            }
            val^=p[i];
        }
    }
    return 0;
}
int main(){
    while(cin>>n)
    {
        clr(p,0);
        for(int i=1;i<=n;i++)
        {
            scanf("%lld",&a[i]);
            s[i]=s[i-1]^a[i];
        }
        if(s[n]==0){
            puts("-1");
            continue;
        }
        int ans=0;
        for(int i=n;i>0;i--)
        {
            ans+=add(a[i]);
        }
        cout<<ans<<endl;
    }
} 
View Code
原文地址:https://www.cnblogs.com/mountaink/p/10348934.html