*HMK 1 for NOIP (130/300)

关于题目

来自曾老的集训期间的特别的周末作业

HMK 1 for NOIP (130/300)

数列的价值(100/100)

题目描述
有一个数列 {ai} ,数列中有 n 个数。对于一个数列,它的价值为 x1^x2^…^xn 。

现在去掉第 i 个数 ai,则它的价值变为 wi 。求 max{wi} 。

输入格式
一行一个正整数 n 。
接下来一行 n 个数,表示这个数列 {ai} 。

输出格式
输出一个数,即输出答案。

样例数据 1
输入  [复制]

3
1 5 2
输出

7
备注
【样例解释】
5 ^ 2 = 7
1 ^ 2 = 3
1 ^ 5 = 4

【数据范围】
对于前 30% 的数据:n<=1000;
对于 100% 的数据:n<=106;ai 和答案均在 int 范围。

签到题,记得加优化。

MY/STD.CPP

#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;

int n;
long long a[1000050],sum,ans=0;

int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    cin >> n; cin>>a[1]; sum=a[1];
    for(int i=2;i<=n;i++)cin>>a[i],sum^=a[i];
    for(int i=1;i<=n;i++)
        ans=max(ans,sum^a[i]);
    cout << ans << endl;
}

图(30/100)

题目描述
给定一张无向图,求满足以下条件的点对 (x,y) 数目:对任意点 z (z!=x,y),边 (x,z) 和 (y,z) 同时存在或同时不存在。

输入格式
一行两个整数 n,m ,分别表示这幅图点数和边的数量。
接下来 m 行,每行两个整数,表示这两个点之间存在一条无向边。保证没有重边和自环。

输出格式
输出一行一个整数,表示答案。

样例数据 1
输入  [复制]

3 3
1 2
2 3
1 3
输出

3
备注
【数据范围】
对于前 30% 的数据:n,m<=500;
对于 100% 的数据:n,m<=106 。

题读错了。。。看成那两个点必须和所有其他点连边。。。

MY.CPP

#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;

const int kkk=1000050;

int n,m,u,v;
int cnt=0;
struct node{int u,v;}side[2*kkk];
void add(int u,int v)
{
    side[++cnt].u = u;
    side[cnt].v = v;
}

long long ans=0;
int jud[kkk];
int main()
{
    cin >> n >> m;
    for(int i=1;i<=m;i++)
    {
        cin >> u >> v;
        add(u,v);
        jud[u]++;jud[v]++;
    }

    for(int i=1;i<=n;i++)
        if(jud[i]>=n-2)
            ans++;

    if(ans)
    {
        ans = ans*(ans-1)/2;
        for(int i=1;i<=m;i++)
            if((jud[side[i].u]>=n-2)&&(jud[side[i].v]>=n-2))
                if((jud[side[i].u]<n-1)||(jud[side[i].v]<n-1))
                    ans -= 1;       
    }

    cout << ans << endl;
}

题解用到了哈希??有点迷

STD.CPP

#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
typedef long long ll;

inline ll Read()
{
    ll X=0,w=1; char ch=0;
    while(ch<'0' || ch>'9') {if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0' && ch<='9') X=(X<<3)+(X<<1)+ch-'0',ch=getchar();
    return X*w;
}

ll a[1000005],b[1000005],v[1000005],n,m,x,y,r,j,ans;

void get_ans()
{
    j=1;
    for(int i=1;i<=n;++i)
        if(b[i]!=b[j]) 
            ans+=(i-j)*(i-j-1)/2,j=i;

    ans+=(n-j)*(n-j+1)/2;   
}

int main()
{
    n=Read();m=Read();ans=0;

    for(int i=1;i<=n;++i)v[i]=r=r*233+17;

    for(int i=1;i<=m;++i)
    {
        x=Read(),y=Read();
        a[x]^=v[y],a[y]^=v[x];
    }

    for(int i=1;i<=n;i++) b[i]=a[i];
    sort(b+1,b+n+1);    get_ans();

    for(int i=1;i<=n;++i) b[i]=a[i]^v[i];
    sort(b+1,b+n+1);    get_ans();

    printf("%lld\n",ans);
}

树链

题目描述
给出一棵树,在这个树上有 m 条链,每条链有一定的价值。现在请你选出若干条链,其中任意两条链所经过的点都无交集,使得得到的价值总和最大。

输入格式
一行两个整数 n,m 。n 表示树的节点树,节点标号为 1,…,n。m 表示链的数量。

接下来 n-1 行,每行两个数 x,y,表示 x,y 间有一条树边。

接下来 m 行,每行三个数 u,v,w。其中 u,v 表示链的两个端点,w 表示这条链具有的价值。

输出格式
输出一行一个整数,即为答案。

样例数据 1
输入  [复制]

7 3
1 2
1 3
2 4
2 5
3 6
3 7
2 3 4
4 5 3
6 7 3
输出

6
备注
【样例解释】
选择第 2,3 条链,价值和为 6 。

【数据范围】
对于前 30% 数据:n,m<=1000;
对于 100% 数据:n,m<=100000;w<=1000。

完全没头绪。。后来才发现是树形Dp。。

STD.CPP

#include<cstdio>
#include<algorithm>
#include<iomanip>
#include<iostream>
#include<cstring>
#include<string>
#include<cmath>
#include<ctime>
#include<cctype>
#include<vector>
using namespace std;
const int N =200005;
int T,n,m,tot,idx,x,y,c[N],l[N],r[N],dp[N],sum[N];
int first[N],deep[N],nxt[N],to[N],f[N][20];
struct node{int x,y,w,lca;}p[N];
vector<int>chain[N];

inline void init(){
    tot=idx=0;
    memset(f,0,sizeof(f));
    memset(first,0,sizeof(first));
    memset(c,0,sizeof(c));
}

inline int Readint(){
    int i=0,f=1;char ch;
    for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
    if(ch=='-') f=-1,ch=getchar();
    for(;ch>='0'&&ch<='9';ch=getchar()) i=(i<<1)+(i<<3)+ch-'0';
    return i*f;
}

inline void Insert(int pos,int val){
    for(int i=pos;i<=n+1;i+=i&(-i))
      c[i]+=val;
}

inline int query(int pos){
    int ret=0;
    for(int i=pos;i;i-=i&(-i)){
        ret+=c[i];
    }
    return ret;
}

inline void add(int x,int y){
    nxt[++tot]=first[x];
    first[x]=tot;
    to[tot]=y;
}

inline void dfs(int u,int fa){
    l[u]=++idx;
    for(int i=1;i<=18;i++){
        f[u][i]=f[f[u][i-1]][i-1];
        if(!f[u][i-1]) break;
    }
    for(int i=first[u];i;i=nxt[i]){
        if(to[i]!=fa){
            deep[to[i]]=deep[u]+1;
            f[to[i]][0]=u;
            dfs(to[i],u);
        }
    }
    r[u]=idx+1;
}

inline int lca(int a,int b){
    if(deep[a]<deep[b]) swap(a,b);
    int k=deep[a]-deep[b];
    for(int i=18;i>=-1;i--)
      if(k&(1<<i)) a=f[a][i];
    if(a==b) return a;
    for(int i=18;i>=-1;i--)
      if(f[a][i]!=f[b][i]) a=f[a][i],b=f[b][i];
    return f[a][0];
}

inline void solve(int u){
    dp[u]=sum[u]=0;
    for(int i=first[u];i;i=nxt[i]){
        int v=to[i];
        if(v!=f[u][0]){
            solve(v);
            sum[u]+=dp[v];
        }
    }
    dp[u]=sum[u];
    for(int i=0;i<chain[u].size();i++){
        int x=p[chain[u][i]].x;
        int y=p[chain[u][i]].y;
        int tmp=query(l[x])+query(l[y]);
        dp[u]=max(dp[u],tmp+sum[u]+p[chain[u][i]].w);
    }
    Insert(l[u],sum[u]-dp[u]);
    Insert(r[u],dp[u]-sum[u]);
}

int main()
{
for(int i=1;i<=n;i++) chain[i].clear();
    n=Readint(),m=Readint();    
    for(int i=1;i<n;i++){
        x=Readint(),y=Readint();
        add(x,y); add(y,x);
    }
    dfs(1,0);
    for(int i=1;i<=m;i++){
        p[i].x=Readint(),p[i].y=Readint();
        p[i].w=Readint();
        p[i].lca=lca(p[i].x,p[i].y);
        chain[p[i].lca].push_back(i);
    }
    solve(1);
    cout<<dp[1]<<endl;
    return 0;
}

感想

多做Dp。。多练题。。。现在只能这样了。。。orz

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值