UOJ Logo heqingyu的博客

博客

普及转提高训练赛第六场

2018-01-07 14:52:55 By heqingyu

这次比赛打GG了。。。

T1:

根据syf教练的说法,n,m<=100,就可以直接上n^3的DP了。。

设f[i]表示前i个灯泡的放置方案数,然后我们可以得出转移方程:

f[i+k]+=f[i],其中k是一个1~a[i]的数

首先把p和a存下来,再记录一下位置,即代码中的id

然后按照p排序一遍,然后枚举灯泡,灯座接口,还有1~a[i],进行转移

但是要记得判断p的位置有没有重叠,重叠的话就可以break了

枚举p数组需要从大到小,不然的话可能会导致重复使用灯泡

code:

#include<bits/stdc++.h>
#define mod 1000000007
using namespace std;
int f[110],p[110],n,m;
struct node{
    int p,a,id; 
}a[110];
bool cmp(node a,node b){return a.p<b.p;}
int main(){
    freopen("physics.in","r",stdin);
    freopen("physics.out","w",stdout);    
    scanf("%d%d",&n,&m);   
    for(int i=1;i<=n;i++)scanf("%d%d",&a[i].p,&a[i].a),a[i].id=i;         
    for(int i=1;i<=m;i++)scanf("%d",&p[i]);    
    sort(a+1,a+n+1,cmp);   
    f[0]=1;    
    for(int i=1;i<=n;i++){    
        for(int j=m-1;j>=0;j--)            
            if(f[j]){            
                for(int ij=1;ij<=a[i].a;ij++){                
                    int k=j+ij;                    
                    if(k>m)    break;                            
                    if(p[k]&&p[k]!=a[i].id)    break;                    
                    f[k]+=f[j];                    
                    f[k]%=mod;                    
                }                
            }            
    }    
    printf("%d",f[m]==0?-1:f[m]);    
    return 0;    
}

T2:

这道题我用了10分钟就凑出来一段代码了。。

注意,是凑出来的。。。

首先仔细观察样例,找到这个轮换和置换的运算方法,然后暴力模拟。。

code

#include<bits/stdc++.h>
using namespace std;
int n,k,p,m,x,a[100010],b[100010];
vector<int> v[100010];
int main(){
    freopen("rotate.in","r",stdin);
    freopen("rotate.out","w",stdout);
    scanf("%d%d%d",&n,&p,&k);    
   for(int i=1;i<=p;i++){    
        scanf("%d",&m);        
        for(int j=1;j<=m;j++)scanf("%d",&x),v[i].push_back(x);        
    }    
    for(int i=1;i<=n;i++)a[i]=i,b[i]=i;    
    for(int i=p;i>=1;i--)    
        for(int j=(int)v[i].size()-1;j>=1;j--){        
            swap(a[b[v[i][j]]],a[b[v[i][j-1]]]);            
            swap(b[v[i][j]],b[v[i][j-1]]);            
        }        
    for(int i=1;i<=n;i++)printf("%d ",a[i]);    
    return 0;    
}

T3:

这道题因为没怎么细看,导致只打了一个骗分。。。

所以还是要好好看题。。

大概就是说暴力模拟竖式除法的过程,然后用一个桶f来记录一下每次的被除数之前是否出现

如果出现,就说明有循环

这题主要是考察细节。。。

code:

#include<bits/stdc++.h>
using namespace std;
int ans,n,d,tot,f[1000010],a[1000010],cnt;
char s[1000010];
void upd(int x){
    if(x<10) s[++cnt]=x+'0';
    else{
        upd(x/10);
        s[++cnt]=x%10+'0';
    }
    return ;
}
int main(){
    freopen("fracdec.in","r",stdin);
    freopen("fracdec.out","w",stdout);
    scanf("%d%d",&n,&d);
    if(n%d==0){printf("%d.0",n/d);return 0;}
    for(int i=1;i<=d;i++)f[i]=-1;
    upd(n/d);
    n%=d;
    f[n]=0; 
    s[++cnt]='.';
    while(1){
        a[++ans]=n*10/d;
        n=n*10%d;        
        if(n==0) break;    
        if(f[n]==-1) f[n]=ans;
        else break;
    }
    if(n==0) for(int i=1;i<=ans;i++) upd(a[i]);
    else{
        for(int i=1;i<=f[n];i++)    upd(a[i]);
        s[++cnt]='(';
        for(int i=f[n]+1;i<=ans;i++) upd(a[i]);
        s[++cnt]=')';
    }    
    for(int i=1;i<=cnt;i++){
        putchar(s[i]);
        if(i%76==0) puts("");
    }
    return 0;
}

T4:

这道题是最后才做的。。。

首先手动分析样例,发现当一条边的承重z小于当前货物w的重量时,是废的

因此只需要把这些边全部给清除掉就可以了,然后判断有几个联通块(用并查集来做)

但是由于时间比较紧,所以没能想到离线排序后做,所以只有30分。。

code:

#include<bits/stdc++.h>
using namespace std;
int n,m,q,x,y,z,fa[100010],ans;
int getfa(int x){return fa[x]==x?x:fa[x]=getfa(fa[x]);}
struct node{
    int x,y,z;
}a[100010];
struct e{
    int x,y;
}w[100010]; 
bool cmp(node x,node y){return x.z>y.z;}
bool cmp1(e x,e y){return x.x>y.x;}
bool cmp2(e x,e y){return x.y<y.y;}
int main(){
    freopen("warehouse.in","r",stdin);
    freopen("warehouse.out","w",stdout);
    scanf("%d%d%d",&n,&m,&q);
    ans=n;
    for(int i=1;i<=m;i++)scanf("%d%d%d",&x,&y,&z),a[i]=(node){x,y,z}; 
    for(int i=1;i<=q;i++)scanf("%d",&w[i].x),w[i].y=i;
    for(int i=1;i<=n;i++)fa[i]=i;
    sort(w+1,w+q+1,cmp1);
    sort(a+1,a+m+1,cmp);
    int now=1;
    for(int i=1;i<=q;i++){
        while(a[now].z>=w[i].x){
            int fx=getfa(a[now].x),fy=getfa(a[now].y);
            if(fx!=fy) fa[fx]=fy,ans--;
            now++; 
        }
        w[i].x=ans;
    }
    sort(w+1,w+q+1,cmp2);
    for(int i=1;i<=q;i++)printf("%d\n",w[i].x);
    return 0;
}

T5:

首先得出一个结论:

1.数越多,and值单调递减

2.数越多,or值单调递增

然后就可以固定右端点r,二分出最大的满足条件的左端点l,再二分出最小的满足条件的l,相减+1即可得到答案(因为单调)

然后判断是否合法可以用st表或者线段树来实现,但是这题常数很大,推荐st表

然而我还是写了线段树。。。用了inline、read、print还有线段树区间l,r赋为全局变量来卡常,终于险A:

code:

#include<bits/stdc++.h>
using namespace std;
const int mod=1e9+7; 
int n,a,b,c,len,d,s[100010],l,r,MAPLE,l1,r1;
struct node{
    int cor,cand,lc,rc,l,r;
}tr[500010];
inline int ask_or(int now){
    if(tr[now].l>=l1&&tr[now].r<=r1) return tr[now].cor;
    int mid=tr[now].l+tr[now].r>>1;
    if(mid>=r1) return ask_or(tr[now].lc);
    else if(mid<l1) return ask_or(tr[now].rc);
    else return ask_or(tr[now].lc)|ask_or(tr[now].rc);
}
inline void build_tree(int l,int r){
    len++;int now=len;
    tr[now].l=l,tr[now].r=r;
    if(l==r){tr[now].cor=tr[now].cand=s[l];return ;}
    int mid=l+r>>1;
    tr[now].lc=len+1;build_tree(l,mid);
    tr[now].rc=len+1;build_tree(mid+1,r);
    tr[now].cand=tr[tr[now].lc].cand&tr[tr[now].rc].cand;
    tr[now].cor=tr[tr[now].lc].cor|tr[tr[now].rc].cor; 
    return ;
}
inline int ask_and(int now){
    if(tr[now].l>=l1&&tr[now].r<=r1) return tr[now].cand;
    int mid=tr[now].l+tr[now].r>>1;
    if(mid>=r1) return ask_and(tr[now].lc);
    else if(mid<l1) return ask_and(tr[now].rc);
    else return ask_and(tr[now].lc)&ask_and(tr[now].rc);
}
inline void read(int &x){
    char ch=getchar();
    while(!isdigit(ch)) ch=getchar();
    x=0;while(isdigit(ch)) x=x*10+ch-'0',ch=getchar();
}
void print(int x){
    if(x<10) putchar(x+'0');
    else{
        print(x/10);
        putchar(x%10+'0'); 
    }
} 
int main(){
    freopen("range.in","r",stdin);
    freopen("range.out","w",stdout);
    read(n);read(a);read(b);read(c);read(d);
    for(int i=1;i<=n;i++)read(s[i]);
    build_tree(1,n);
    for(int i=1;i<=n;i++){
        int r=i,l=1,ans=0; 
        while(l<=r){
            int mid=l+r>>1;
            l1=mid;r1=i; 
            int t=ask_and(1); 
            int k=ask_or(1);
            if(t<=b&&k>=c) l=mid+1,ans=mid;
            else r=mid-1;
        } 
        l=1; 
        int cnt=ans;ans=i+1; 
        r=i;
        while(l<=r){
            int mid=l+r>>1;
            l1=mid;
            r1=i;
            int t=ask_and(1);
            int k=ask_or(1);
            if(t>=a&&k<=d) r=mid-1,ans=mid;
            else l=mid+1;
        } 
        if(cnt-ans+1>0) MAPLE+=cnt-ans+1;
        MAPLE%=mod;
    }
    print(MAPLE%mod); 
    return 0;
}

T6:

专门为XMR爸爸准备的题啊。。。

我只理概念:

期望:

对于每种概率,乘上这种可能的值,他们的和就是期望

没了。。。

评论

oi_loser
代码用 ```c++ code ``` 显示效果更佳。
  • 2018-01-07 20:56:59
heqingyu
谢谢
  • 2018-01-08 09:41:43