浪潮杯第十届大学生山东省ACM程序设计竞赛

A题:Calandar

注意审题,每月30天,每周5天,直接算出两个日期相差天数,然后取余计算就行了。

#include
#include
#include
#include
#include

using namespace std;

unordered_mapbook;
char s[55];

int main()
{
    book[1]="Monday";
    book[2]="Tuesday";
    book[3]="Wednesday";
    book[4]="Thursday";
    book[5]="Friday";
    int T;
    cin>>T;
    while(T--)
    {
        int y1,m1,d1,y2,m2,d2;
        scanf("%d%d%d%s",&y1,&m1,&d1,s);
        int pos1,pos2;
        if(s[0]=='M') pos1=1;
        else if(s[0]=='T'&&s[1]=='u') pos1=2;
        else if(s[0]=='W') pos1=3;
        else if(s[0]=='T') pos1=4;
        else pos1=5;
        long long a=y1*12*30ll+m1*30+d1;
        scanf("%d%d%d",&y2,&m2,&d2);
        long long b=y2*12ll*30+m2*30+d2;
        long long dx=b-a;
        if(dx>0)
        {
            dx%=5;
            pos1=(pos1+dx-1)%5+1;
        }
        else
        {
            dx=-dx;
            dx%=5;
            pos1=(pos1-dx+4)%5+1;
        }
        cout<

C题:Wandering Robot

因为初始点为(0,0)所以经过一轮操作后,|x|+|y|一定变大,所以最大值一定是在第一轮或者最后一轮里取得,所以先找出第一轮的最大值和第一轮结束后的x、y坐标然后计算出第k-1轮结束后的坐标,再把最后一轮模拟一遍求出最大值。

#include
#include
#include
#include

using namespace std;

const int maxn=100005;

char s[maxn];

int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        int n,k;
        scanf("%d%d",&n,&k);
        scanf("%s",s+1);
        long long x=0,y=0;
        long long ans=0;
        for(int i=1;i<=n;i++)
        {
            if(s[i]=='U') y++;
            else if(s[i]=='D') y--;
            else if(s[i]=='L') x--;
            else x++;
            ans=max(ans,abs(x)+abs(y));
        }
        y*=(k-1);x*=(k-1);
        for(int i=1;i<=n;i++)
        {
            if(s[i]=='U') y++;
            else if(s[i]=='D') y--;
            else if(s[i]=='L') x--;
            else x++;
            ans=max(ans,abs(x)+abs(y));
        }
        printf("%lld\n",ans);
    }
    return 0;
}

D题:Game on a Graph

倒数第n-1条边是谁取的就是谁输

#include
#include
#include
#include

using namespace std;

const int maxn=100005;

int a[maxn];

int main()
{
    int T;
    cin>>T;
    while(T--)
    {
         int n;
         scanf("%d",&n);
         for(int i=0;i

E题:BaoBao Loves Reading

树状数组+前缀和

书架上有n本书,书桌可以放k本,给出n本书的索引,看书时要把书架上的书取到书桌上,如果本来书桌上就有那本书就不用再从书架上取,如果书桌上没有,并且书桌满了,就要把最近读的书留下,把读的最早的书放回书桌上。分别输出k=1到k=n的情况下把书从书架取到书桌上的次数。

当书桌可以放k本书的时候,对于每一本书,如果跟这本书相同索引并且离这本书最近的那本书已经在书架上了,则不需要再从书架上取,也就是在原序列中,如果第i本书和与它前面并且索引相同的最近的一本书中间的书的种类数小于k则不需要从书桌上取。

用d[i]表示书架存i本数时的操作步数,然后读入书的索引后,从前往后枚举每一本书,如果这本书与它前面离它最近的书的中间还有m本书,则d[1]~d[m]的值都加1;如果它前面没有与它相同索引的书,则说明不管书架容量是多少,这本书一定要被拿到书架上,所以把d[1]~d[n]都加1,每次都是对一个区间的操作,可以反方向用差分优化,中间有m本的时候就把d[m]加1(d[m]+1,d[0]-1),0本的时候把d[n]加1(d[n]+1,d[0]-1),最后求一个前缀和。

还有一个地方就是如何求出某本书与它前面相同索引的不重复的书的本数。也就是不同数字的个数,很容易联想到树状数组,树状数组的功能是动态维护前缀和。在这里可以用来查询比一个数小的数有几个。遍历第i本书的时候就把第i本书加1,然后把它前面离它最近的那本书-1,然后i的前缀和就是前i本不重复的书的本数。

代码如下:

#include
#include
#include
#include
#include

using namespace std;

const int maxn=100005;

int a[maxn];
int s[maxn];
int d[maxn];
int c[maxn];
int n;

mapbook;

int lowbit(int n)
{
    return n&-n;
}

int query(int x)
{
    int ans=0;
    for(;x;x-=lowbit(x))ans+=c[x];
    return ans;
}

void add(int x,int y)
{
    for(;x<=n;x+=lowbit(x))c[x]+=y;
}

int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        book.clear();
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        for(int i=1;i<=n;i++) s[i]=d[i]=c[i]=0;
        for(int i=1;i<=n;i++)
        {
            if(!book[a[i]])
            {
                s[i]=0;
                add(i,1);
            }
            else
            {
                add(book[a[i]],-1);
                add(i,1);
                s[i]=query(i)-query(book[a[i]]);
            }
            book[a[i]]=i;
        }

        for(int i=1;i<=n;i++)
        {
            if(s[i]==0) d[n]++;
            else d[s[i]-1]++;
        }
        for(int i=n-1;i>=1;i--) d[i]+=d[i+1];
        for(int i=1;i<=n;i++)
        {
            if(i==n) printf("%d\n",d[n]);
            else printf("%d ",d[i]);
        }
    }
    return 0;
}

F题:Stones in the Bucket

两种操作,一种是直接拿掉一,一种是把1拿到另一个上面,最终的结果一定是所有数的平均数

#include
#include
#include

using namespace std;

const int maxn=100005;

int a[maxn];

int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        int n;
        scanf("%d",&n);
        long long sum=0;
        for(int i=0;i

H题:Tokens on the Segments

贪心+优先队列

贪心策略:优先按照左端点排序,然后优先选择右端点最小的线段

先将线段按左端点从小到大排序,然后从左端点最小的线段开始选点,如果左端点相同就优先选择右端点小的线段,用now记录现在选到了哪个点,然后每次选完都更新其他线段的左端点。

最坏时间复杂度 :O(N \times 10^9 \times logN )

但是数据弱,还是可以过的

#include
#include
#include
#include

using namespace std;

#define l first
#define r second

typedef pair pii;

const int N=1e5+5;

int main(){
    int _;for(scanf("%d",&_);_;_--){
        priority_queue,greater> q;
        int n;scanf("%d",&n);
        for(int i=1,a,b;i<=n;i++) scanf("%d%d",&a,&b),q.push({a,b});
        
        int ans=0,now=0;
        while(q.size()){
            auto t=q.top();q.pop();
            if(t.l>now){ now=t.l,ans++;continue; }
            else if(t.r>now) q.push({now+1,t.r});
        }
        cout<

shc代码:

每条线段入队一次出队一次,复杂度O(nlogn)

#include
using namespace std;
#define fir first
#define sec second
const int N=(int)1e5+5;
int T,n,i,now,ans;
paira[N];
priority_queueq;

int main(){
	//freopen("1.in","r",stdin);
	scanf("%d",&T);
	while(T--){
		scanf("%d",&n);
		for(i=1;i<=n;i++) scanf("%d%d",&a[i].fir,&a[i].sec);
		now=1,ans=0;
		sort(a+1,a+n+1);
		for(i=1;i<=n;i++){
			if(a[i].fir>now){
				while(q.size() && now

L题:Median

Floyd

先用Floyd算法把所有能确定的大小关系都确定出来,然后枚举每个数,统计比他小的数和比它大的数分别有几个,如果比它小的和比它大的数都小于n/2说明这个数可能是中位数。比赛时一直wa。。现在我已经搞不懂当时为啥会wa了。。

#include
#include
#include
#include

using namespace std;

const int maxn=105;

int book[maxn][maxn];

unordered_mapmm;

int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        mm.clear();
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=0;i<=n;i++)
            for(int j=0;j<=n;j++)
                book[i][j]=0;

        int flag=1;
        while(m--){
            int a,b;
            scanf("%d%d",&a,&b);
            if(a==b) flag=0;
            else
            {
                book[a][b]=1;
                book[b][a]=-1;
            }
        }
        if(!flag){
            for(int i=0;i

 

M题:Sekiro

n除以k次2,每次都向上取整,因为n是1e9范围,所以31次肯定能除成1,,所以如果k>31直接输出1,否则模拟一遍

#include
#include
#include

using namespace std;

int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        int n,k;
        scanf("%d%d",&n,&k);
        if(n==0){
            printf("0\n");
            continue;
        }
        if(k>40){
            printf("1\n");
        }else{
            while(k--)
            {
                n=(n+1)/2;
            }
            printf("%d\n",n);
        }
    }
    return 0;
}

B题:

组合数学+记忆化搜索/DP

杨辉三角预处理组合数

 

题意:n盏灯,每次改变m盏,改变k次能够达到目标状态的方案数

灯的初始状态与目标状态对比,计算出需要改变状态的灯的数目

假设gao(a,k)表示a盏灯的状态需要改变,操作k次之后有0盏灯需要改变的方案数,利用组合数推导一下就OK了

#include
#include
#include

using namespace std;

const int N=105;
const int mod=998244353;

int pos[N];
int C[N][N];
int n,k,m;
int book[N][N];

int gao(int a,int k){
	if(a==0&&k==0) return book[a][k]=1;
	if(a&&k==0) return 0;
	if(book[a][k]!=-1) return book[a][k];
	int ans=0;
	for(int i=0;i<=min(a,m);i++){
		if(m-i>n-a) continue;
		ans=(ans+1ll*gao(a-i+m-i,k-1)*C[a][i]%mod*C[n-a][m-i]%mod)%mod;
	}
	return book[a][k]=ans;
}

int main(){
	for(int i=0;i

K题:

打表找规律

快速幂、局部暴力

打表发现a为奇数的时候只有x=a一个解

a为偶数时:

          x大于p时:a^x\mod 2^p = 0,所以只需求出x^a \mod 2^p =0的解的个数

                              若x为奇数,x的二进制位最后一位一定是1,所以模2^p一定不为0所以x为偶数时才可能有解

                               因此解的个数为:x^a中2的因子个数大于等于p个的解的个数

                               因此只需求x中2的因子个数大于等于 \lceil \frac{p}{a} \rceil 即是 2^{\lceil \frac{p}{a} \rceil} 的倍数的个数

      x小于p时暴力算。

#include

using namespace std;

int mod;

int qmi(int a,int b){
	int ans=1%mod;
	while(b){
		if(b&1) ans=1ll*ans*a%mod;
		a=1ll*a*a%mod;
		b>>=1;
	}
	return ans;
}

int main(){
	int _;for(cin>>_;_;_--){
		int a,p;cin>>a>>p;
		if(a&1) cout<<1<

G题:

从原序列的最后一个数开始看

最后一个数交换的顺序一定是按照 a_{i}a_{\frac{i}{2}}a_{\frac{i}{2^2}}\cdot \cdot \cdot 的顺序交换,因此从最后一个数开始依次确定is_max的值即可

#include
#include

using namespace std;

const int N=1e5+5;

int a[N],b[N],c[N];
int ans[N];
bool flagg;

int get(int u){
    
    int p=u,cnt=0;
    while(p>=1){
        c[++cnt]=b[p];
        p>>=1;
    }
    
    int x=a[u];
    int flag=true;
    int t=1;
    //假设为0
    for(int i=1;i<=cnt;i++){
        if(c[i]==x){
            t=i;
            break;
        }
        if(c[i]>1;i>=1) b[k]=c[i];
            return 0;
        }
        else if(c[t+1]<=x){
            b[u]=x;
            for(int i=1,k=u>>1;i>=1) b[k]=c[i];
            return 0;
        }
    }
    
    //假设为1
    flag=true;
    for(int i=1;i<=cnt;i++){
        if(c[i]==x){
            t=i;
            break;
        }
        if(c[i]>x){
            flag=false;
            break;
        }
    }
    
    if(flag&&c[t]!=x){
        flagg=0;return -1;
    }
    
    if(flag){
        if(t==cnt){
            b[u]=x;
            for(int i=1,k=u>>1;i>=1) b[k]=c[i];
            return 1;
        }
        else if(c[t+1]>=x){
            b[u]=x;
            for(int i=1,k=u>>1;i>=1) b[k]=c[i];
            return 1;
        }
    }
    
    flagg=0;
    return -1;
}

int main(){
    int _;for(scanf("%d",&_);_;_--){
        int n;scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        for(int i=1;i<=n;i++) scanf("%d",&b[i]);
        
        flagg=true;
        for(int i=n;i>0&&flagg;i--) ans[i]=get(i);
        
        if(flagg) for(int i=1;i<=n;i++) printf("%d",ans[i]);
        else printf("Impossible");
        
        puts("");
    }
    return 0;
}

J题:

Dijkstra+欧拉回路

模板题

#include
#include
#include
#include
#include

#define x first
#define y second

using namespace std;

typedef pairpii;
typedef pairpli;

const int N=2*150005;
const long long inf=1e18;

int ma_x[N],ma_y[N];
int get_id(int a,int b){
    int ans=(a-1)*a/2+b;
    ma_x[ans]=a;
	ma_y[ans]=b;
    return ans;
}

int h[N],e[N],ne[N],w[N],idx;
void add(int a,int b,int c){e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;}

int n;
int pre[N];
long long dis[N];
bool st[N];

long long dijkstra(){
    
    int start=1,ed=get_id(n,n);
    
    priority_queue,greater>q;
    for(int i=1;i<=ed;i++) pre[i]=st[i]=0;
    
    for(int i=1;i<=ed;i++) dis[i]=inf;
    q.push({0,start});dis[start]=0;
    
    while(q.size()){
        auto t=q.top();q.pop();
        long long d=t.first;
        int id=t.second;
        
        if(st[id]) continue;
        st[id]=true;
        
        for(int i=h[id];~i;i=ne[i]){
            int y=e[i];
            if(dis[y]>d+w[i]){
                pre[y]=id;
                dis[y]=d+w[i];
                q.push({dis[y],y});
            }
        }
    }
    return dis[ed];
}

int res[N],cnt;
bool use[N];

void del(int u,int v){
    for(int i=h[u];~i;i=ne[i]){
        int y=e[i];
        if(y==v){
            use[i]=use[i^1]=true;
            return;
        }
    }
}

// stacksta;
// void dfs( int u ) {
//     sta.push(u);
//     for( register int i = h[u]; ~i; i = ne[i] ) {
//         if( use[i] ) continue;
//         use[i] = use[ i ^ 1 ] = true;
//         dfs(e[i]);
//         break;
//     }
// }

// void fleury(int st) {
//     sta.push(st);
//     while(!sta.empty() ) {
//         int flag = 0, u = sta.top(); sta.pop();
//         for( register int i = h[u]; ~i; i = ne[i] ) {
//             if( use[i] ) continue;
//             flag = 1; break;
//         }
//         if( !flag ) res[++cnt]=u;
//         else dfs(u);
//     }
// }

void dfs(int u){
    for(int i=h[u];~i;i=ne[i]){
        if(use[i]) continue;
        use[i]=use[i^1]=true;
        dfs(e[i]);
    }
    res[++cnt]=u;
}

int main(){
	//freopen("1.in","r",stdin); 
    int _;for(scanf("%d",&_);_;_--){
        cnt=0;idx=0;
        scanf("%d",&n);
        for(int i=0;i<=n*n/2*3;i++) h[i]=-1;
        for(int i=0;i<=n*n*3;i++) use[i]=false;
        long long ans=0;
        for(int i=1;i=1;i--) printf("%d %d ",ma_x[res[i]],ma_y[res[i]]);
        puts("");
    }
    return 0;
}
#include
#include
#include
#include

#define x first
#define y second

using namespace std;

typedef pairpii;
typedef pairpli;

const int N=180005;
const long long inf=1e18;
const int dx[]={-1,1,0,0,-1,1};//上 下 左 右 左上 右下
const int dy[]={0,0,-1,1,-1,1};
/*
0 1->0;
2 1->1;
1 0->2;
1 2->3;
0 0->4;
2 2->5;

*/
const int book[3][3]={
   {4,0,0}, 
   {2,0,3},
   {0,1,5}
};

pii ma[N];
inline int get_id(pii a){
    int ans=(a.x-1)*a.x/2+a.y;
    ma[ans]=a;
    return ans;
}

inline pii get_id(int u){
    return ma[u];
}

int n;
int pre[N];
long long dis[N];
bool st[N];

int w[6*N];

inline int bb(int u,int v){
    pii a=get_id(u);
    pii b=get_id(v);
    int ddx=b.x-a.x;
    int ddy=b.y-a.y;
    return u+book[ddx+1][ddy+1]*N;
}

long long dijkstra(){
    
    int start=get_id({1,1}),ed=get_id({n,n});
    
    priority_queue,greater>q;
    for(int i=1;i<=ed;i++) st[i]=false;
    
    for(int i=1;i<=ed;i++) dis[i]=inf;
    q.push({0,start});dis[start]=0;
    
    while(q.size()){
        auto t=q.top();q.pop();
        long long d=t.first;
		int id=t.second;
        
        if(st[id]) continue;
        st[id]=true;
        
        pii pos=get_id(id);
        for(int i=0;i<6;i++){
            int posx=pos.x+dx[i];
            int posy=pos.y+dy[i];
            
            if(posy>posx) continue;
            if(posx<=0||posy<=0) continue;
            if(posx>n||posy>n) continue;
            
            int ne_id=get_id({posx,posy});
            if(dis[ne_id]>d+w[bb(id,ne_id)]){
                pre[ne_id]=id;
                dis[ne_id]=d+w[bb(id,ne_id)];
                q.push({dis[ne_id],ne_id});
            }
            
        }
    }
    return dis[ed];
}

int res[N],cnt;

void dfs(int u){
    pii pos=get_id(u);
    for(int i=0;i<6;i++){
        int posx=pos.x+dx[i];
        int posy=pos.y+dy[i];
        
        if(posy>posx) continue;
        if(posx<=0||posy<=0) continue;
        if(posx>n||posy>n) continue;
        
        int ne=get_id({posx,posy});
        if(!w[bb(u,ne)]) continue;
        w[bb(u,ne)]=0;
        w[bb(ne,u)]=0;
        
        dfs(ne);
    }
    res[++cnt]=u;
}

int main(){
	//freopen("1.in","r",stdin); 
    int _;for(scanf("%d",&_);_;_--){
        cnt=0;
        scanf("%d",&n);
        long long ans=0;
        for(int i=1;i=1;i--) printf("%d %d ",get_id(res[i]).x,get_id(res[i]).y);
        puts("");
    }
    return 0;
}

J题:

最近公共祖先(LCA)+线段树

 

处理出1与2、2与3、、、i与i+1、n-1与n之间的路径上最大的节点和最小的节点

用线段树维护区间和,并支持如下操作:

1.将区间[l,r]之间的值变为0

2.将第x个值变为1

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

1.计算长度为1的区间个数,有n个

2.计算长度大于等于2的区间:

   若[l,r]符合要求,则所有相邻的两个的路径的最小值的最小值一定为l,最大值的最大值一定为r

#include
#include
#include
#include
#include

using namespace std;

const int N=3e5+5,M=N*2;
const int INF=0x3f3f3f3f;

int h[N],e[M],ne[M],idx;

void add(int a,int b){e[idx]=b,ne[idx]=h[a],h[a]=idx++;}

int depth[N], fa[N][20];
int n;

int fa_min[N][20],fa_max[N][20];

void bfs(){
    for(int i=1;i<=n;i++) depth[i]=INF;
    queueq; q.push(1); depth[1]=1;
    while(q.size()){
        int t=q.front();q.pop();
        for(int i=h[t];~i;i=ne[i]){
            int y=e[i];
            if(depth[y]>depth[t]+1){
                depth[y]=depth[t]+1;
                q.push(y);
                
                fa[y][0]=t;
                fa_min[y][0]=fa_max[y][0]=t;
                
                for(int k=1;k<=19;k++){
                    fa[y][k]=fa[fa[y][k-1]][k-1];
                    fa_min[y][k]=min(fa_min[y][k-1],fa_min[fa[y][k-1]][k-1]);
                    fa_max[y][k]=max(fa_max[y][k-1],fa_max[fa[y][k-1]][k-1]);
                }
            }
        }
    }
}

int minv[N],maxv[N];

void lca(int a, int b,int u){
    minv[u]=min(a,b);maxv[u]=max(a,b);
    if(depth[a]=depth[b]){
        minv[u]=min(minv[u],fa_min[a][i]),maxv[u]=max(maxv[u],fa_max[a][i]);
        a=fa[a][i];
    }
    if(a==b) return;
    for(int i=19;~i;i--) if(fa[a][i]!=fa[b][i]){
        minv[u]=min(minv[u],fa_min[a][i]),maxv[u]=max(maxv[u],fa_max[a][i]);
        minv[u]=min(minv[u],fa_min[b][i]),maxv[u]=max(maxv[u],fa_max[b][i]);
        a=fa[a][i],b=fa[b][i];
    }
    minv[u]=min(minv[u],fa_min[a][0]),maxv[u]=max(maxv[u],fa_max[a][0]);
}

struct node{int l,r,sum;bool flag;}tr[4*N];

void build(int u,int l,int r){
    tr[u]={l,r,0,false};
    if(l==r){ return; }
    int mid=l+r>>1;
    build(u<<1,l,mid),build(u<<1|1,mid+1,r);
}

void pushup(int u){ tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum; }

void pushdown(int u){
    auto &root=tr[u],&left=tr[u<<1],&right=tr[u<<1|1];
    if(root.flag){
        left.sum=right.sum=0;
        left.flag=right.flag=true;
        root.flag=false;
    }
}
void modify(int u, int l, int r, int d){
    if(tr[u].l>=l&&tr[u].r<=r){
        if(d) tr[u].sum=1,tr[u].flag=false;
        else tr[u].sum=0,tr[u].flag=true;
        return;
    }
    pushdown(u);
    int mid=tr[u].l+tr[u].r>>1;
    if(l<=mid) modify(u<<1,l,r,d);
    if(r>mid) modify(u<<1|1,l,r,d);
    
    tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum;
}

int query(int u, int l, int r){
    if(tr[u].l>=l&&tr[u].r<=r) return tr[u].sum;
    
    pushdown(u);
    
    int mid=tr[u].l+tr[u].r>>1;
    int sum = 0;
    if(l<=mid) sum+=query(u<<1,l,r);
    if(r>mid) sum+=query(u<<1|1,l,r);
    return sum;
}

void init(){
    for(int i=0;i<=n;i++) h[i]=-1;
    idx=0;
    build(1,1,n);
}

int main(){
    int _;for(scanf("%d",&_);_;_--){
        scanf("%d",&n);init();
        for(int i=1;is1,s2;
        s1.push(1);
        s2.push(1);
        
        long long ans=n;
        for(int i=2;i<=n;i++){
            while(minv[s1.top()]>=minv[i]) s1.pop();
            modify(1,s1.top(),i,0);
            if(s1.top()==minv[i]) modify(1,minv[i],minv[i],1);
            s1.push(i);
            while(maxv[s2.top()]<=maxv[i]) s2.pop();
            if(maxv[i]==i) ans+=query(1,s2.top(),i); 
            s2.push(i);
        }
        printf("%lld\n",ans);
    }
    return 0;
}

 

你可能感兴趣的:(ACM,山东省赛)