2018年4月17日训练日记

昨晚的手速练习7:

好几道题目貌似以前做过,而且这套题难度不大,时间足够的话应该完全可以AK。坑的是H题水题没发现,等AC的时候发现比赛已经结束三分钟...

A:男女生交错排列,多的都排在后面。注意男生多先排男生,女生多先排女生。WA三次,晕。

B:删除最小数量的数,使剩余的数中的最大数<=最小数*2。从小到大排序,然后维护满足条件的最小l,扫一遍数组,求出n-(i-l+1)的最小值即可。

C:这题比赛的时候没做纯粹是因为题干太长不想读,实际上题目并不难。就是文章第r1行c1列的位置移动到文章第r2行c2列,显然最终答案一定是直接移动到最后的位置或者先移动到最短长度的那一行,再移动到目标位置。暴力找一遍即可。

AC代码:

#include <bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
#define lson i*2,l,m
#define rson i*2+1,m+1,r
using namespace std;
const int mo=1e9+7;
const int mx=410;
int lb(int x){return x&(-x);}
int n,m,c[mx],b[mx],ans,k;
int a[mx],f,len[mx];
int dp[mx][mx];
int main() {
    //ios::sync_with_stdio(false);
    int t,i,j,k,l,q,x,y,ss,h;
    int cas=1,flag,f1;
    freopen("input.txt","r",stdin);
    freopen("output.txt","w",stdout);
     while(scanf("%d",&n)!=EOF)
     {
        // memset(dp,0,sizeof(dp));
         f=0;k=0;
         for(int i=1;i<=n;i++)
         {
             scanf("%d",&a[i]);
         }
         int c1,c2,r1,r2;//c lie  r hang
         scanf("%d %d %d %d",&r1,&c1,&r2,&c2);
         ans=inf;
         for(int i=1;i<=n;i++)
         {
             int mi=min(i,min(r1,r2));
             int ma=max(i,max(r1,r2));
             int tmp=c1;
         for(int j=mi;j<=ma;j++)
         {
             tmp=min(tmp,a[j]+1);
         }
         tmp=abs(r1-i)+abs(r2-i)+abs(c2-tmp);
         ans=min(ans,tmp);
         }
     printf("%d\n",ans);
     }
    return 0;
}

D:就是给相同值的贺卡两两配对。问最后能不能配对成功。记录一下每个元素出现的次数,然后顺便匹配一下,如果每个值都出现了偶数次,就直接输出结果。否则-1。刚开始不知道怎么想的cin输入printf输出,果断tle。。。迷。。。

E:给出每个活动结束的时间,需要多少人在这之前准备多少天。求最少多少人就可以把所有活动都准备好。教科书般的线段树区间更新区间最大值。注意下标要编好,否则就会无脑WA~

AC代码:

#include <bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
#define lson i*2,l,m
#define rson i*2+1,m+1,r
using namespace std;
const int maxn=10010;
const int mo=1e9+7;
int n,m,nn,mm,k,h,f;
int ans,tmp;
int sm[maxn];
bool flag;
int dd[]={31,30,31,30,31,31,28,31,30,31,30,31,31,30,31,30,31};
ll sum[maxn*4],c[maxn];
ll addv[maxn*4];
int id;
void PushDown(int i,int num)
{
    if(addv[i])
    {
        sum[i*2]+=addv[i]*(num-(num/2));
        sum[i*2+1]+=addv[i]*(num/2);
        addv[i*2]+=addv[i];
        addv[i*2+1]+=addv[i];
        addv[i]=0;
    }
}
void PushUp(int i)
{
    sum[i]=sum[i*2]+sum[i*2+1];
}
void build(int i,int l,int r)
{
    if(l==r)
    {
        addv[i]=0;
        sum[i]=0;
        return ;
    }
    int m=(l+r)/2;
    build(lson);
    build(rson);
    PushUp(i);
}
void update(int ql,int qr,long long add,int i,int l,int r)
{
    if(ql<=l&&r<=qr)
    {
        addv[i]+=add;
        sum[i]+=(ll)add*(r-l+1);
        return ;
    }
    PushDown(i,r-l+1);
    int m=(l+r)/2;
    if(ql<=m) update(ql,qr,add,lson);
    if(m<qr) update(ql,qr,add,rson);
    PushUp(i);
}
ll query(int ql,int qr,int i,int l,int r)
{
    if(ql<=l&&r<=qr)
    {
        return sum[i];
    }
    PushDown(i,r-l+1);
    int m=(l+r)/2;
    ll res=0;
    if(ql<=m) res+=query(ql,qr,lson);
    if(m<qr) res+=query(ql,qr,rson);
    return res;
}
int main(){
    sm[0]=0;
    for(int i=1;i<=17;i++)
    sm[i]=sm[i-1]+dd[i];
    freopen("input.txt","r",stdin);
    freopen("output.txt","w",stdout);
    scanf("%d",&n);
        memset(sum,0,sizeof(sum));
        memset(addv,0,sizeof(addv));
        memset(c,0,sizeof(c));
        for (int i=1;i<=n;i++){
            int m,d,p,t;
            scanf("%d%d%d%d",&m,&d,&p,&t);
            int tp=sm[m+3]+d;
            t--;
            update(tp-t,tp,(ll)p,1,1,1000);
        }
        ll ans=0;
        for(int i=1;i<1000;i++)
        {
        ll x=query(i,i,1,1,1000);
        if(x>ans) ans=x;
        }
        printf("%lld\n",ans);
        return 0;
    }

F:给两个字符串S,T,你要用和T串相同的字母及其数量来重新构造S,使S改变的字母数最少。有多个答案输出字典序最小的那个。(这是关键)

显然T串和S串每个字母数量差绝对值的和/2就是最少改变的数量。那么问题就变成了如何构造字典序最小的S。首先数组sum1和

数组sum2分别记录S串T串每个字母出现的数量。

我们再加一个数组jud[s[i]-'A']表示在i之前S串s[i]这个位置的字母出现的次数。从前往后遍历S串,如果字符sum1[s[i]-'A']>sum2[s[i]-'A'],并且jud[s[i]-'A']==sum2[s[i]-'A']的话我们就找一个字典序最小的sum1[ch]<sum2[ch]的字符,用ch来替换当前字符s[i]。小于的话我们就从字典序最小开始找,找到比当前字符字典序大就停。如果找到了就用找到的字母替换当前的字母,否则记录jud[s1[i]-'A']++,也就是说我们先不换这个字母。当jud[s[i]-'A']==sum2[s[i]-'A']的时候就说明之前的这个字符已经数量足够了,因此这个字母必须换成其他字母。思路十分巧妙,赛后补题。。。

AC代码:

#include <bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
#define lson i*2,l,m
#define rson i*2+1,m+1,r
using namespace std;
const int mo=1e9+7;
const int mx=100010;
int lb(int x){return x&(-x);}
int n,m,c[mx],ans,k;
char s1[mx],s2[mx];
int jud[30];
int sum1[30],sum2[30];
int gcd(int x,int y){while(x^=y^=x^=y%=x);return y;}
int main() {
    //ios::sync_with_stdio(false);
    int t,i,j,k,l,q,x,y,ss,h;
    int cas=1,flag,f1;
    freopen("input.txt","r",stdin);
    freopen("output.txt","w",stdout);
     while(scanf("%s%s",s1,s2)!=EOF)
    {
        //getchar();
        int mi=0;
        memset(sum1,0,sizeof(sum1));
        memset(sum2,0,sizeof(sum2));
        memset(jud,0,sizeof(jud));
        n=strlen(s1);
        for(i=0;i<n;i++) {
            sum1[s1[i]-'A']++;
            sum2[s2[i]-'A']++;
        }
        for(i=0;i<=25;i++) mi+=abs(sum1[i]-sum2[i]);
        printf("%d\n",mi/2);
        mi=mi>>1;
        int f=0;
        for(i=0;i<n;i++)
        {
        if(sum1[s1[i]-'A']>sum2[s1[i]-'A'])
        {
        if(jud[s1[i]-'A']==sum2[s1[i]-'A'])
             for(j=0;j<=25;j++) {
               if(sum1[j]<sum2[j]){sum1[j]++;
                 sum1[s1[i]-'A']--;
                 s1[i]='A'+j;
                 break;
                 }
                 }
          else {
          	for(j=0;j<=25;j++)
          	{
          		if(j>s1[i]-'A'){
          			jud[s1[i]-'A']++;
          			break;
          			}
          		  if(sum1[j]<sum2[j]) {
                 {sum1[j]++;
                 sum1[s1[i]-'A']--;
                 s1[i]='A'+j;
                 break;
                 }
                 }
                 }
            }
            }
            }
          printf("%s\n",s1);
    }
    return 0;
}

G:水题,求a[0]+a[0+1*3]+a[0+2*3]+...

和 a[1]+a[1+1*3]+a[1+2*3]+...

和a[2]+a[2+1*3]+a[2+2*3]+...三个和中的最大值。

H:这道题超级水。。。比赛的时候就没仔细读题。。。就是求x的数量和y的数量,然后把多的那个多多少就输出几遍。。。AC之后已经是比赛结束之时。。。醉了!!!

I:定义一个序列a,这个序列的第一个元素a[1]随意,从a[2]开始满足a[i]=a[i-1]+p*(-1)^i;(2<=i<=k)就是波动序列啊。。。经典dp

dp[i][j]表示这个序列的前一个元素的下标是j,这一个元素下标是i时满足条件的最长序列长度。

显然dp[i][j]=dp[j][k]+1(a[k]==a[i]),且i>j>k;

然后dp一下取最大值就可以了。赛后补得题,比赛的时候没仔细看这道题。

AC代码:

#include <bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
#define lson i*2,l,m
#define rson i*2+1,m+1,r
using namespace std;
const int mo=1e9+7;
const int mx=4010;
int lb(int x){return x&(-x);}
int n,m,c[mx],b[mx],ans,k;
int a[mx],f,len[mx];
int dp[mx][mx];
int main() {
    //ios::sync_with_stdio(false);
    int t,i,j,k,l,q,x,y,ss,h;
    int cas=1,flag,f1;
    //freopen("input.txt","r",stdin);
    //freopen("output.txt","w",stdout);
     while(scanf("%d",&n)!=EOF)
     {
         ans=1;
        // memset(dp,0,sizeof(dp));
         f=0;k=0;
         for(int i=1;i<=n;i++)
         {
             scanf("%d",&a[i]);
         }
         for(int i=1;i<=n;i++)
         {
             int k=-1;
         for(int j=1;j<i;j++)
         {
             if(k==-1) dp[i][j]=2;
             else dp[i][j]=dp[j][k]+1;
             if(a[j]==a[i]) k=j;
             ans=max(ans,dp[i][j]);
         }
         }
     printf("%d\n",ans);
     }
    return 0;
}

J:给你8*8的棋盘,W为白色棋子,B为黑色棋子,你可以进行一个操作:把某一行最后一个棋子放到改行第一个,也就是前面的棋子都后移一位,把原来这一行最后的棋子放在改行第一个位置。然后给你一种摆放方法,求你能不能通过操作使每一行、每一列相邻的棋子颜色都不一样。如果可以输出YES,否则输出NO。

直接根据题意模拟即可。挺简单的啊,为什么比赛的时候做的人不多呢。。。是不是因为没读懂题。。。

K:给你3*3的棋盘,除了对角线上的元素,每个格子都有一个元素,要你填上对角线上的数字使其每一行、列、对角线的元素和相等。这道题还浪费了我不少时间。。。其实挺好推的,在纸上写一下就不难发现a[2][2]=(a[3][1]+a[3][2]-a[2][1]-a[2][3]+a[1][2]+a[1][3])/2;然后就可以填了。

L:水题。题意就相当于给你01串,去除第一个出现的0,如果没有0就随便去个1。

这次没AK纯粹是因为时间不够哇。。。不过F和I确实也有一定难度。理不清思路就肯定做不出来了。

资料:

整理了图论的一些模板(连通性问题,2-SAT,差分约束,网络流,二分图) 一整理还发现真忘了不少。。。(整理的越多,说明忘得越多),翻了自己以前写的博客以及图论专题的题目来顺便复习一下。

还整理了一些常用的技巧。。。目前模板整理了60多页。。。继续整理。。。

猜你喜欢

转载自blog.csdn.net/lsd20164388/article/details/79976123