[3.16校内训练赛]

这次一个学长出题....结果我把dij写成了大顶的,就说复杂度那么科学怎么T了.........真的丢人

-------------------------------

A.给定一个长度为n的序列,你要求出从那个位置开始连续数n个数,得到的序列最大(先比第一位,再第二位..)。n<=2000000

题解:第一眼想到的是可以把每个数拆开来计数排序+dc3后缀数组,应该可过。

但是此题还有一个非常妙的解法。

假设目前最优的开头是i,你要判断开头为j的是否更优,那么你可以找到第一位不同的位k,即s[i+k]!=s[j+k],

这时候,如果s[j+k]<[i+k],那么以i开头的比j优,i+1的也会比j+1的优.....i+k的同样比j+k更优,所以我们可以直接让j=j+k+1,继续计算即可。复杂度O(n)

#include<iostream>
#include<cstdio>
#include<cstring>
#define MAXN 2000000
using namespace std;
inline int read()
{
    int  x=0,f=1;char ch=getchar();
    while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘) f=-1;ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘; ch=getchar();}
    return x*f;
}

int n,s[2*MAXN+5];

int main()
{
    freopen("minecraft.in","r",stdin);
    freopen("minecraft.out","w",stdout);
    n=read();
    for(int i=1;i<=n;i++)
        s[i]=s[i+n]=read();
    int k,i=1,j=2;
    for(;j<=n;)
    {
        for(k=0;s[j+k]==s[i+k]&&k<n;k++);
        if(s[j+k]>s[i+k])i=(s[j+k]>s[j])?j+k:j;
        j=j+k+1;
    }
    for(int j=0;j<n;j++)printf("%d ",s[j+i]);
    return 0;
}

B.有n个数s1..sn,你要从中删除一些数,使得最后满足si=i(第i个数是i)的数尽量多。n<=100000

做法:考虑一个n^2的dp,即f[i]=max(f[j])+1,但必须满足 1)i>j   2)si-sj<=i<j  3)si>sj

但是我们可以发现,如果满足了2和3两个条件,那么第一个条件一定满足。

又因为si-sj<=i-j   -> si-i<=sj-j 所以我们把si-i和si其中的一个排序,并且把另一个离散之后存到线段树里加速dp就好啦。

复杂度nlogn

#include<iostream>
#include<cstdio>
#include<algorithm>
#define N 131072
using namespace std;
inline int read()
{
    int  x=0,f=1;char ch=getchar();
    while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘) f=-1;ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘; ch=getchar();}
    return x*f;
}

int n;
int s[100005],f[100005],l[100005],p[100005],T[N*2+5],l2[100005];

void renew(int x,int ad)
{
    x+=N;T[x]=max(T[x],ad);
    for(x>>=1;x;x>>=1)T[x]=max(T[x<<1],T[x<<1|1]);
}

int query(int l,int r)
{
    int sum=0;
    for(l+=N-1,r+=N+1;l^r^1;l>>=1,r>>=1)
    {
        if(~l&1)sum=max(sum,T[l+1]);
        if( r&1)sum=max(sum,T[r-1]);
    }
    return sum;
}

bool cmp(int x,int y)
{
    return s[x]<s[y]||(s[x]==s[y]&&p[x]<p[y]);
}

int main()
{
    freopen("fivethree.in","r",stdin);
    freopen("fivethree.out","w",stdout);
    n=read();
    for(int i=1;i<=n;i++)s[i]=read();
    for(int i=1;i<=n;i++)l2[i]=p[i]=s[i]-i;
    sort(l2+1,l2+n+1);int j=1;
    for(int i=2;i<=n;i++)if(l2[i]!=l2[i-1]) l2[++j]=l2[i];
    for(int i=1;i<=n;i++)p[i]=j-(lower_bound(l2+1,l2+j+1,p[i])-l2)+1;
    int K=0;
    for(int i=1;i<=n;i++)
        if(s[i]-i<=0) l[++K]=i;
    sort(l+1,l+K+1,cmp);
    s[0]=-2000000000;
    for(int ii=1;ii<=K;ii++)
    {
        int i=l[ii];
        f[i]=query(1,p[i])+1;
        if(s[i]!=s[l[ii+1]])
            for(int j=ii;j&&s[l[j]]==s[l[ii]];j--)
                renew(p[l[j]],f[l[j]]);
    }
    printf("%d\n",query(1,n));
    return 0;
}

C.有一个n个点m条边的图,有边权,你要从第1个点到第n个点,但是你必须先到2...k+1这些点去搞事情。特殊地,有一些限制(a,b),表示必须在a搞完事情之后才能在b搞事情,求搞完所有事情到达第n个点至少要走的距离。n<=20000,m<=200000,k<=20

题解:很容易发现其实和图没什么关系,而且k的范围比较小,考虑壮压dp。

先用dij预处理出两两之间和每个点到起点终点的距离

然后用f[i][j]表示最后走到第i个点,状态是j的最小距离,转移即可。状态数最多2^20*20≈20000000,转移复杂度20,但实际并没有这么多状态,并且题目有2s,所以还是比较科学的。

交了一个大顶堆,真的丢人

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#define INF 2100000000
using namespace std;
inline int read()
{
    int  x=0,f=1;char ch=getchar();
    while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘) f=-1;ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘; ch=getchar();}
    return x*f;
}

int n,m,K,cnt=0;
int p[25],l[25],to,v[25],rk[1000005],num[1000005];
int dis[25][25],d[20005],head[20005];
int f[22][1100000];
bool mark[20005];
struct edge{
    int to,next,w;
}e[400005];
struct node{int x,f;};
class cmp{public:bool operator()(node a,node b){return a.f>b.f;}};
priority_queue<node,vector<node>,cmp>q;

void ins(int f,int t,int w)
{
    e[++cnt].next=head[f];head[f]=cnt;
    e[cnt].to=t;e[cnt].w=w;
}

void work(int num,int from)
{
    while(!q.empty())q.pop();
    memset(d,0x3f3f3f,sizeof(d));
    memset(mark,0,sizeof(mark));
    d[from]=0;q.push((node){from,0});
    while(!q.empty())
    {
        int u=q.top().x;q.pop();if(mark[u])continue;mark[u]=1;
        for(int i=head[u];i;i=e[i].next)
            if(d[u]+e[i].w<d[e[i].to])
            {
                d[e[i].to]=d[u]+e[i].w;
                q.push((node){e[i].to,d[e[i].to]});
            }
    }
    for(int i=1;i<=K;i++)dis[num][i]=d[i+1];
    dis[num][K+1]=d[n];
}

int main()
{
    freopen("revenge.in","r",stdin);
    freopen("revenge.out","w",stdout);
    n=read();m=read();K=read();to=1<<K;
    p[1]=1;for(int i=2;i<=K;i++)p[i]=p[i-1]<<1;
    for(int i=1;i<=m;i++)
    {
        int u=read(),v=read(),w=read();
        ins(u,v,w);ins(v,u,w);
    }
    m=read();
    for(int i=1;i<=m;i++)
    {int u=read(),v=read();l[v-1]|=p[u-1];}
    work(0,1);if(K==0)return 0*printf("%d",dis[0][K+1]);
    for(int i=1;i<=K;i++)
        work(i,i+1);
    for(int i=1;i<=K;i++)
        for(int j=0;j<to;j++)
            f[i][j]=INF;
    for(int i=1;i<=K;i++) if(!l[i])
        f[i][p[i]]=dis[0][i];
    for(int i=1;i<to;i++)
    {
        for(int j=1;j<=K;j++)
            if(f[j][i]<INF)
                for(int k=1;k<=K;k++)
                    if((l[k]&i)==l[k]&&(!(i&p[k])))
                    {
                        int into=i|p[k];
                        f[k][into]=min(f[k][into],f[j][i]+dis[j][k]);
                    }
    }
    int ans=INF;
    for(int i=1;i<=K;i++)ans=min(ans,f[i][to-1]+dis[i][K+1]);
    cout<<ans;
    return 0;
}

D.有n个数,你可以任选一个区间,并且用其中的任意一个数与这个区间的次大值异或起来,求最大异或值。n<=50000

题解:这道题是最好想出来的.....

对于每个数,往两边二分到第一个比它大的数,再向两边二分到第二个比它大的数。

假设序列是1 3 4 2 6 1 8,那么对于数2,它二分到的四个点分别是3,4,6,8,假设称为l2,l1,r1,r2

那么在区间(l1,r2)和(l2,r1)内,这个数显然都是次大值。然后我们对于每个查询到的区间都在可持久化TRIE树上查询一个和它异或最大的值,更新一下答案。

二分时候可以用线段树来check,在可持久化TRIE上查询是log,所以复杂度是nlog^2n

#include<iostream>
#include<cstdio>
#include<cstring>
#define MAXN 2000005
#define N 65536
#define INF 2100000000
using namespace std;
inline int read()
{
    int  x=0,f=1;char ch=getchar();
    while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘) f=-1;ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘; ch=getchar();}
    return x*f;
}

int s[50005];
struct TRIE{
    int son[2];
    int size;
}T[MAXN];
int t[N*2+5],c[35],rt[50005],cnt=0,n,ans=0;

void renew(int x,int ad)
{
    t[x+=N]=ad;
    for(x>>=1;x;x>>=1)t[x]=max(t[x<<1],t[x<<1|1]);
}

int query(int l,int r)
{
    int sum=0;
    for(l+=N-1,r+=N+1;l^r^1;l>>=1,r>>=1)
    {
        if(~l&1)sum=max(sum,t[l+1]);
        if( r&1)sum=max(sum,t[r-1]);
    }
    return sum;
}

void ins(int num)
{
    memset(c,0,sizeof(c));rt[num]=++cnt;
    for(int x=s[num],len=0;x;c[++len]=(x&1),x>>=1);
    for(int i=30,x=rt[num-1],nx=rt[num];i;i--)
    {
        T[nx].son[c[i]]=++cnt;T[nx].son[!c[i]]=T[x].son[!c[i]];
        x=T[x].son[c[i]];nx=T[nx].son[c[i]];
        T[nx].size=T[x].size+1;
    }
}

void calc(int l,int r,int xx)
{
    if(l>r)return;
    memset(c,0,sizeof(c));for(int x=xx,len=0;x;c[++len]=(x&1),x>>=1);
    int sum=0;
    for(int i=rt[l-1],j=rt[r],k=30,l=1<<29;k;k--,l>>=1)
    {
        c[k]=!c[k];
        int to=(T[T[j].son[c[k]]].size-T[T[i].son[c[k]]].size>0)?c[k]:1-c[k];
        if(to)sum|=l;j=T[j].son[to];i=T[i].son[to];
    //    cout<<i<<" "<<j<<" "<<to<<" "<<sum<<endl;
    }
    ans=max(ans,sum^xx);
}

int get(int rr,int xx,int spec)
{
    int l=1,mid,r=--rr,q=rr+1;
    if(!rr)return spec;
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(query(mid,rr)<xx)q=mid,r=mid-1;
        else l=mid+1;
    }
    return q;
}

int get2(int ll,int xx,int what)
{
    int l=++ll,r=n,mid,q=ll-1;
    if(ll>n)return what;
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(query(ll,mid)<xx)q=mid,l=mid+1;
        else r=mid-1;
    }
    return q;
}

int main()
{
    freopen("lock.in","r",stdin);
    freopen("lock.out","w",stdout);
    n=read();int mx=0;
    for(int i=1;i<=n;i++)
        {s[i]=read();ins(i);renew(i,s[i]);mx=max(mx,s[i]);}
    for(int i=1;i<=n;i++)
    {
        if(s[i]==mx)continue;
        int l1=get(i,s[i],2)-1,l2=get(l1,s[i],l1);
        int r1=get2(i,s[i],n-1)+1,r2=get2(r1,s[i],r1);
    //    cout<<i<<" "<<l1<<" "<<l2<<" "<<r1<<" "<<r2<<"!!!!!!!"<<endl;
        calc(l2,min(r1+1,n),s[i]);calc(max(0,l1-1),r2,s[i]);
    }
    cout<<ans;
    return 0;
}
时间: 2024-10-15 03:11:44

[3.16校内训练赛]的相关文章

[3.10校内训练赛]

真的报警啦,hzwer又出一堆丧题虐人啦..... ------------------------------------------- A.[poj-1190]生日蛋糕 要做一个m层的蛋糕,每一层有高度和半径,且要分别比它上面的那一层的高度和半径大至少一,给定总体积n,求最小的侧面和顶上的面积之和m<=20,n<=10000 搜索....但是要加上比较强的剪枝. 1.如果此时的半径和高度无法建出剩余体积那么大的蛋糕,剪掉.这种情况我们不考虑半径和高度的减小,直接用((r-1)^2+(h-1

[3.3校内训练赛]

我好菜啊都不会 --------------- A.[百练2812] 恼人的青蛙 给丁一张r*c的图,上面有最多n个青蛙脚印,一个青蛙行走的路线是一条直线,且间隔距离相同,最少行走3个点. 求可能的青蛙走过的踩过最多脚印的路线的脚印数量. r,c,n<=5000 做法:枚举两个点,check一下. 加一些剪枝,比如如果目前这条直线即使可行也不会比答案优秀,我们就可以退出了. 还可以把点按照横坐标排序,每次如果因为横坐标相差过大导致出现剪枝1的情况,就可以不搜前面这个点了,因为后面的点相差显然更大

[3.9校内训练赛]

不要纠结为什么3.9的比赛比3.10的还迟做完,你们首先得知道这场是ditoly出的 ditoly丧病出题人啊丧病出题人啊丧病出题人啊 本机上 T1 std3.000s卡过,我的3.1s被卡了 T3 总共2s,std写的treap跑的飞快,我写的替罪羊2.1s也被卡了 听说学校机子T1只要2s,学校机子真神奇,MBA还是不适合跑程序,低压i5伤不起啊...... 分割线------------------------------------ A.一个n*m的网格图,每个格子都有一个0-9的权值,

最后一周第二天训练赛之第二题

试题: B - B Time Limit:3000MS     Memory Limit:0KB     64bit IO Format:%lld & %llu Submit Status Practice SPOJ ICODER Description Mathews uses a brand new 16-bit instruction processor. (Yeah i am being sarcastic!). It has one register (say R) and it su

Dream_Chaser队训练赛第一场 I题

Dream_Chaser队训练赛第一场 I题 题目来自2012成都区域赛 I - Count Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Submit Status Practice HDU 4472 Description Prof. Tigris is the head of an archaeological team who is currently in charge of a

2017后期 第 1 场训练赛

题目依次为 NKOJ 上 P3496 P4236 P3774 P2407 1.数三角形 方法很多, 比如推出三边 x y z 的限制关系, 然后加加减减得到计算式子 不过也可以用观察法, 暴力计算出 n 为 1 至 13 对应的结果为: 0 0 0 1 3 7 13 22 34 50 70 95 125 相邻两数差为: 0 0 1 2 4 6 9 12 16 20 25 30 这些相邻两数相邻差又为: 0 1 1 2 2 3 3 4 4 5 5 找到规律了, 如果结果第 i 项为第 i - 1

HDU-4930 Fighting the Landlords 多校训练赛斗地主

只需要判断一个回合就可以了,枚举判断可以一次出完所有牌或者大过对面的牌的可能,注意的是4张相同的牌带两张牌的话是可以被炸弹炸的. #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <queue> #include <algorithm> #include <cstdlib> #include <ioma

CSU-ACM2014暑假集训基础组训练赛(1) 解题报告

•Problem A HDU 4450                 水题,签到题 水题..没啥好说的.给大家签到用的. 1 #include <cstdio> 2 int main(){ 3 int n,a,ans; 4 while(scanf("%d",&n),n){ 5 ans = 0; 6 for(int i = 0;i < n;i++){ 7 scanf("%d",&a); 8 ans += a*a; 9 } 10 pr

cumt训练赛题解

2017年4月3日 cumt2017春季--训练赛(1) A.HihoCoder 1339 (dp) 思路: 比较清晰呢,就是个dp吧.定义一下状态,dp[i][j]:前i个骰子,扔出点数和为j的方案数.然后不就很好写了嘛递推式,dp[i][j] = dp[i - 1][j - k](1<=k<=6). 好像题就做完了诶,可是窝wa了两发什么鬼.第一发爆int,第二发爆longlong,这里的trick就是方案数可能非常大,所以这题的应该把状态定义为dp[i][j]:前i个骰子,扔出点数和为j