UPC-专题-基本算法

两年前退役 现在重拾OI算法进击ACM 就让博客园陪我走完全程吧
结论就写在前面 方便自己复习 当然你想看看也可以看看 主要还是用来复习的时候提醒我自己的
别问为什么有的题目没有 问就是咕了 希望两年之后能在区赛上被高中机房大佬虐

一定要注意变量类型 这也就是为什么当年看到过那么多#define int long long

可以不用带入函数和过程的变量尽量别代入 不然最后你知都不知道怎么错的

多种状态+n<=20应当快速想到状压DP这种算法 不是<=500必不可能是Floyd

汉诺塔类问题可打表找结果 n塔m盘代码也放在了下面

对于游戏类的问题 出题人肯定还是想让你多去找找性质和规律的

读入尽量不要使用字符串和普通的单个字符读入 问题很多 必要时一定要调试验证读入准确性

还是得训练自己的OI思维 现在实在是太弱了 算法不行 思维也不行 既然是二维的图就得想到用方便的XY坐标表示 打打表规律就出来了

像七夕祭这种结论死都证不出来的题 可以放到最后 然后猜结论 反正只要硬点对了AC后就是大佬

逆序对很多时候会排上难以想象的用场 以后可以盲猜一手逆序对

取中位数可以用两个堆来维护 一个大根一个小根 统称对顶堆 询问时将两根顶交换到一样为止 似乎可以维护第k大的数
中位数是两个堆相差个数最大为1 第k大是大根堆存k-1个数 小根堆堆顶即为第k大 中途保证小根堆堆顶大于大根堆堆顶

有时候一些简单的数学原理可以自己手推一下 毕竟ACM主要考的还是思维 而不像OI一样逐渐数据结构化 万一推对了呢

遇到问题无法解决时 可以尝试逆向思维 反正你也做不出来 不试白不试

模拟时一定要细心 因为模拟考的就是代码能力和细心

区间排序时不一定都按照左端点 右端点也是有可能的

二维的最大连续子列其实就是一维套上前缀和以及行数枚举

——————————————————————————————分割线——————————————————————————————

问题 A: 【快速幂】a^b

快速幂板子题 但是因为一个long long和膜数带入过程WA了很多次 引以为戒

#include <bits/stdc++.h>
using namespace std;
#define LL long long
LL n,m,mo;
LL ksm(LL x,LL y)
{
    LL ans=1,num=x%mo;if(y==0)return 1;
    while(y)
    {
        if(y&1!=0)ans=ans*num%mo;
        y>>=1;num=num*num%mo;
    }
    return ans%mo;
}
int main()
{
	scanf("%lld%lld%lld",&n,&m,&mo);
	printf("%lld\n",ksm(n,m)%mo);
	return 0;
}

问题 B: 64位整数乘法

高精度乘法和膜法板子题
我知道有别的方法 但是我太菜了只会高精度

#include <bits/stdc++.h>
using namespace std;
#define LL long long
LL n,m,mo,sum;
int a[100],b[100],c[100],an,bn,cn,pps,num;
int main()
{
	scanf("%lld%lld%lld",&n,&m,&mo);
	n%=mo;m%=mo;LL fmo=mo;
	while(n){a[++an]=n%10;n/=10;}
	while(m){b[++bn]=m%10;m/=10;}
	while(fmo){num++;fmo/=10;}
	for(int i=1;i<=an;i++)
	    for(int j=1;j<=bn;j++)
	        c[i+j-1]+=a[i]*b[j];
	for(int i=1;i<=(an+bn);i++)
	    c[i+1]+=c[i]/10,c[i]%=10;
	if(c[an+bn])cn=an+bn;else cn=an+bn-1;
	//for(int i=cn;i>0;i--)printf("%d",c[i]);
	for(int i=1;i<=(cn/2);i++)swap(c[i],c[cn+1-i]);
	//for(int i=1;i<=cn;i++)printf("%d",c[i]);
	while(pps<cn)
	{
	    pps++;
	    sum=sum*10+c[pps];
	    //printf("%lld\n",sum);
	    if(sum>=mo)sum%=mo;
	}printf("%lld",sum);
}

问题 C: 最短Hamilton路径

一开始竟然想到Floyd
最后是个状压DP 也是很基础的板子了

#include<bits/stdc++.h>
using namespace std;
int a[40][40],n,s[21][1200000];
int main()
{
	scanf("%d",&n);memset(s,17,sizeof(s));s[1][1]=0;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)scanf("%d",&a[i][j]);
	for(int i=1;i<(1<<n);i++)
		for(int j=1;j<=n;j++)
			if((i>>(j-1))&1)
			for(int k=1;k<=n;k++)
				if(((i>>(k-1))&1)&&(j!=k))
					s[j][i]=min(s[j][i],s[k][i^(1<<(j-1))]+a[k][j]);
	printf("%d",s[n][(1<<n)-1]);
	return 0;
}

问题 D: Raising Modulo Numbers

快速幂板子题套了个取模 不还是个板子题???

#include<bits/stdc++.h>
#define LL long long
using namespace std;
LL ans;int n,m,mo,a,b;
LL ksm(int x,int y)
{
	LL sum=1,a=x%mo;if(y==0)return 1;
	while(y)
	{
		if(y&1!=0)sum=sum*a%mo;
		y>>=1;a=a*a%mo;
	}return sum%mo;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d",&mo,&m);ans=0;
		for(int j=1;j<=m;j++){scanf("%d%d",&a,&b);ans=(ans+ksm(a,b))%mo;}
		printf("%lld\n",ans%mo);
	}
	return 0;
}

问题 E: Strange Towers of Hanoi

老汉诺塔问题了 记得以前n个柱子m个盘是拿DP+贪心写的 这里就直接套通项公式n个2^n了

#include<bits/stdc++.h>
using namespace std;
int f[20],pps,a;
int ksm(int x,int y)
{
	int s=1,p=x;if(y==0)return 1;
	while(y)
	{
		if(y&1!=0)s*=p;
		y/=2;p*=p;
	}return s;
}
int main()
{
	f[1]=1;pps=1;a=2;
	for(int i=2;i<=12;i++)
	{
		if(pps<=a)pps++,f[i]=f[i-1]+ksm(2,a-1);
		else a++,pps=2,f[i]=f[i-1]+ksm(2,a-1);
	}
	for(int i=1;i<=12;i++)printf("%d\n",f[i]);
	return 0;
}

n塔m盘的DP算法

#include<bits/stdc++.h>
using namespace std; 
int dp[maxn][maxn];
signed main()
{
    int n,m;n=31,m=31;dp[2][1]=1;//初始化两座塔的情况
    for(int i=3;i<=m;++i)dp[i][1]=1;//初始化只有一个盘的情况
    for(int i=2;i<=n;++i)dp[3][i]=2*dp[3][i-1]+1;//初始化只有三座塔的情况
    /*计算m塔问题,m>3 */
    for(int i=4;i<=m;++i){
        for(int j=2;j<=n;++j)
		{
            dp[i][j]=dp[i-1][j];
            for(int k=1;k<j;++k)dp[i][j]=min(2*dp[i][k]+dp[i-1][j-k],dp[i][j]);
        }
    }
    for(int i=3;i<=m;++i)
        for(int j=1;j<=n;++j) printf("%d",dp[i][j]);
}

问题 F: 费解的开关

记得当年做这道题的时候一脸懵逼
然而现在再做一遍还不是一样
枚举第一行的是否开关灯的状态
之后每一个关掉的灯都由下方的按钮来打开
一开始读入用的是字符串 调试了挺久的 还是整数读入香 虽然麻烦了亿点

#include<bits/stdc++.h>
using namespace std;
int n,a[10][10],b[10][10],p=10,s;bool pps;
void dfs(int x,int y)
{
	if(x<5){dfs(x+1,y^(1<<(x+1)));dfs(x+1,y);}
	if(x==5)
	{
		int ans=0;
		for(int i=1;i<=5;i++)
			for(int j=1;j<=5;j++)b[i][j]=a[i][j];
		for(int i=1;i<=5;i++)
			if((y>>i)&1)b[1][i-1]^=1,b[1][i]^=1,b[1][i+1]^=1,b[2][i]^=1,ans++;
		for(int i=1;i<=4;i++)
			for(int j=1;j<=5;j++)
			if(!b[i][j])b[i+1][j-1]^=1,b[i+1][j]^=1,b[i+1][j+1]^=1,b[i][j]^=1,b[i+2][j]^=1,ans++;
		if(ans>6)return;bool pass=true;
		for(int i=1;i<=5;i++)
			for(int j=1;j<=5;j++)if(!b[i][j])pass=false;
		if(pass)p=min(p,ans),pps=true;
	}
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		pps=false;p=10;
		for(int j=1;j<=5;j++)
		{
			scanf("%d",&s);//printf("%s",s);
			for(int k=5;k>=1;k--)a[j][k]=s%10,s/=10;
		}
		dfs(0,0);if(pps)printf("%d\n",p);else printf("-1\n");
	}
	return 0;
}

问题 G: 激光炸弹

二维前缀和 v=a[i][j]-a[i-r][j]-a[i][j-r]+a[i-r][j-r]

#include<bits/stdc++.h>
using namespace std;
int n,m,x,y,v,X,Y,a[5100][5100],ans;
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){scanf("%d%d%d",&x,&y,&v);a[x+1][y+1]+=v;X=max(x+1,X);Y=max(y+1,Y);}
	for(int i=1;i<=X;i++)
		for(int j=1;j<=Y;j++)a[i][j]+=a[i-1][j]+a[i][j-1]-a[i-1][j-1];
	for(int i=min(X,m);i<=X;i++)
		for(int j=min(Y,m);j<=Y;j++)ans=max(ans,a[i][j]-a[i-min(X,m)][j]-a[i][j-min(Y,m)]+a[i-min(X,m)][j-min(Y,m)]);
	printf("%d",ans);
	return 0;
}

问题 H: Tallest Cow

开头-- 结尾++ 最后用前缀和统计即可 为了判重 用了一个结构体

#include<bits/stdc++.h>
using namespace std;
int n,p,h,r,s[10100];
struct ss{int l,r;}a[10100];
bool cmp(ss x,ss y){if(x.l==y.l)return x.r<y.r;else return x.l<y.l;}
int main()
{
	scanf("%d%d%d%d",&n,&p,&h,&r);
	for(int i=1;i<=r;i++){scanf("%d%d",&a[i].l,&a[i].r);if(a[i].l>a[i].r)swap(a[i].l,a[i].r);}
	sort(a+1,a+r+1,cmp);
	for(int i=1;i<=r;i++)
	{
		if((a[i].l==a[i-1].l)&&(a[i].r==a[i-1].r))continue;
		if(a[i].l==a[i].r)continue;
		s[a[i].l+1]--;s[a[i].r]++;
	}
	for(int i=1;i<=n;i++)s[i]+=s[i-1];
	for(int i=1;i<=n;i++)printf("%d\n",s[i]+h);
	return 0;
}

问题 I: 递归/非递归实现组合型枚举

递归入门题

#include<bits/stdc++.h>
using namespace std;
int n,m;
void dfs(int x,int y,int z)
{
	if(x<m)
	for(int i=z+1;i<=n;i++)
		if(!(y&(1<<i)))dfs(x+1,y^(1<<i),i);
	if(x==m)
	{
		int pps;
		for(int i=1;i<=n;i++)
			if(y&(1<<i)){printf("%d",i);pps=i+1;break;}
		for(int i=pps;i<=n;i++)
			if(y&(1<<i))printf(" %d",i);printf("\n");
	}
}
int main()
{
	scanf("%d%d",&n,&m);dfs(0,0,0);
	return 0;
}

问题 J: 分形之城

转来转去挺恶心的 看来我还是没有找到当年的那种思维方式 找出规律后带入旋转改变的坐标即可

#include<bits/stdc++.h>
#define LL long long
using namespace std;
struct ss{LL x,y;}axy,bxy;
LL T,n,a,b;
ss work(LL x,LL y)
{
    if(x==0){ss pps;pps.x=0,pps.y=0;return pps;}
    LL len=1<<(x-1),mo=1<<(x*2-2);ss pps=work(x-1,y%mo);
    LL X=pps.x,Y=pps.y,z=y/mo;
    if(z==0){ss ans;ans.x=Y,ans.y=X;return ans;} 
    else if(z==1){ss ans;ans.x=X;ans.y=Y+len;return ans;}
    else if(z==2){ss ans;ans.x=X+len;ans.y=Y+len;return ans;}
    else{ss ans;ans.x=2*len-Y-1;ans.y=len-X-1;return ans;}
}
int main()
{
    scanf("%lld",&T);
    while(T)
    {
        T--;scanf("%lld%lld%lld",&n,&a,&b);
        axy=work(n,a-1);bxy=work(n,b-1);
        double ans=10.0*sqrt(1.0*(axy.x-bxy.x)*(axy.x-bxy.x)+1.0*(axy.y-bxy.y)*(axy.y-bxy.y));
        printf("%.0lf\n",ans);
    }
    return 0;
}

问题 L: Best Cow Fences

似乎代码被卡了精度 至少在我能力范围内是A不了了

问题 M: 货仓选址

找中位数 其实应该也可以用前缀和和后缀和来暴力搜索

#include<bits/stdc++.h>
#define LL long long
using namespace std;
int n,a[100010];double ans,mid;
int main()
{
    scanf("%d",&n);for(int i=1;i<=n;i++)scanf("%d",&a[i]);sort(a+1,a+n+1);
    if(n%2==0)mid=1.0*(a[n/2]+a[n/2+1])/2;else mid=a[n/2+1];
    for(int i=1;i<=n;i++)ans+=abs(mid-a[i]);printf("%lld",(LL)ans);
    return 0;
}

问题 N: 七夕祭

当年的一个神仙题目 一直不知道结论是怎么证出来的 不过OI讲究的就是过了就行 反正大佬硬点是什么就是什么
有了结论 这道题其实就是跑两边纸牌传递 COPY一下就好了

#include<bits/stdc++.h>
#define LL long long
using namespace std;
int n,m,t,a,b,x[100010],y[100010],c[100010],d[100010];LL sumx,sumy,ans;bool flag1,flag2;
int main()
{
    scanf("%d%d%d",&n,&m,&t);flag1=false;flag2=false;
    for(int i=1;i<=t;i++)scanf("%d%d",&a,&b),x[a]++,y[b]++,sumx++,sumy++;
    if(sumx%n)flag1=true;if(sumy%m)flag2=true;sumx/=n;sumy/=m;
    for(int i=2;i<=n;i++)c[i]=c[i-1]+x[i]-sumx;for(int i=2;i<=m;i++)d[i]=d[i-1]+y[i]-sumy;
    sort(c+1,c+n+1);sort(d+1,d+m+1);
    if(!flag1)for(int i=1;i<=n;i++)ans+=abs(c[i]-c[(n+1)/2]);
    if(!flag2)for(int i=1;i<=m;i++)ans+=abs(d[i]-d[(m+1)/2]);
    if(flag1&&flag2){printf("impossible");return 0;}
    if(flag1)printf("column ");if(flag2)printf("row ");if(!flag1&&!flag2)printf("both ");
    printf("%lld",ans);
    return 0;
}

问题 O: 奇数码问题

别问 问就是求逆序对个数 问就是证不出来

#include<bits/stdc++.h>
using namespace std;
int n,s[1000010],a[1000010];long long ans1,ans2;
int lowbit(int x){return(x&(-x));}
void updata(int x,int v){for(int i=x;i<=n*n;i+=lowbit(i))a[i]+=v;}
int work(int x){int ans=0;for(int i=x;i;i-=lowbit(i))ans+=a[i];return ans;}
int main()
{
	while(~scanf("%d",&n))
	{
		for(int i=1;i<=n*n;i++)scanf("%d",&s[i]);
		for(int i=1;i<=n*n;i++)if(s[i])updata(s[i],1),ans1+=work(n*n)-work(s[i]);
		memset(a,0,sizeof(a));memset(s,0,sizeof(s));
		for(int i=1;i<=n*n;i++)scanf("%d",&s[i]);
		for(int i=1;i<=n*n;i++)if(s[i])updata(s[i],1),ans2+=work(n*n)-work(s[i]);
		ans1%=2;ans2%=2;memset(s,0,sizeof(s));
		if(ans1==ans2)printf("TAK\n");else printf("NIE\n");
		ans1=0;ans2=0;memset(a,0,sizeof(a));
	}
}

问题 P: Running Median

动态取中位数 第一次知道维护中位数可以用两个堆(一个小根一个大根)
就疯狂堆堆 取中位数的时候就将两个堆的堆顶交换到相同为止
不过缺点就是没办法回溯

#include<bits/stdc++.h>
using namespace std;
int T,n,ns,nb,p,a[10010],s[10010],b[10010],ans[10010],pps;
void adds(int x){s[++ns]=x;int now=ns;while(now>1){if(s[now]>=s[now/2])return;swap(s[now],s[now/2]);now/=2;}}
void addb(int x){b[++nb]=x;int now=nb;while(now>1){if(b[now]<=b[now/2])return;swap(b[now],b[now/2]);now/=2;}}
void downs()
{
	int now=1;
	while(now*2<=ns)
	{
		now*=2;if(s[now]>=s[now/2]&&s[now+1]>=s[now/2])return; 
		if(now<ns&&s[now+1]<s[now])now++;swap(s[now/2],s[now]);
	}
}
void downb()
{
	int now=1;
	while(now*2<=nb)
	{
		now*=2;if(b[now]<=b[now/2]&&b[now+1]<=b[now/2])return;
		if(now<nb&&b[now+1]>b[now])now++;swap(b[now/2],b[now]);
	}
}
void work()
{
	while(true)
	{
		if(s[1]==b[1]){ans[++pps]=s[1];return;}
		swap(s[1],b[1]);downs();downb();
	}	
}
int main()
{
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d",&p,&n);for(int i=1;i<=n;i++)scanf("%d",&a[i]);
		ans[++pps]=a[1];s[++ns]=a[1];b[++nb]=a[1];
		for(int i=2;i<=n;i++)
		{
			adds(a[i]);addb(a[i]);
			if(i%2)work();
		}
		printf("%d %d\n",p,pps);
		for(int i=1;i<=pps;i++){if(i%10==1)printf("%d",ans[i]);else printf(" %d",ans[i]);if(i%10==0)printf("\n");}
		if(pps%10)printf("\n");memset(s,0,sizeof(s));memset(b,0,sizeof(b));ns=0;nb=0;pps=0;
	}
}

问题 Q: Ultra-QuickSort

本质还是求逆序对个数 这个排序速度吊打快排

#include<bits/stdc++.h>
#define LL long long
using namespace std;
int n,tree[500010];LL ans;
struct ss{int x,y;}a[500010];
bool cmp(ss x,ss y){return x.x<y.x;}
int lowbit(int x){return(x&(-x));}
void add(int x,int v){for(int i=x;i<=n;i+=lowbit(i))tree[i]+=v;}
int get(int x){int pps=0;for(int i=x;i;i-=lowbit(i))pps+=tree[i];return pps;}
int main()
{
	while(~scanf("%d",&n))
	{
		if(n==0)return 0;
		for(int i=1;i<=n;i++)scanf("%d",&a[i].x),a[i].y=i;sort(a+1,a+1+n,cmp);
		for(int i=1;i<=n;i++){add(a[i].y,1);ans+=get(n)-get(a[i].y);}
		printf("%lld\n",ans);ans=0;memset(tree,0,sizeof(tree));
	}
}

问题 S: 国王游戏

假设一个人左手为a右手为b 另一个人左手为c右手为d
那么如果硬点ab>cd 就有a/d>c/b (a/d为第一个人在前面的时候后一个人的权值 c/b为第二个人同上)
即不难发现左右手乘积越大越应该放在后面

#include<bits/stdc++.h>
using namespace std;
struct ss{int l,r;}a[10010];
bool cmp(ss x,ss y){return x.l*x.r<y.l*y.r;}
int n,pps,ansp,s[50010],cache[50010],ans[50010];
void cheng(int x)
{
    for(int i=pps;i;i--)s[i]*=x;for(int i=1;i<=pps;i++)s[i+1]+=s[i]/10,s[i]%=10;
    while(s[pps+1]>=10)pps++,s[pps+1]+=s[pps]/10,s[pps]%=10;
    if(s[pps+1])pps++;
}
void chu(int x)
{
    int yls=0,num=0;bool flag=false,fuck=false;
    for(int i=pps;i;i--)
    {
        num=num*10+s[i];if(fuck)cache[++yls]=num/x,num%=x;
        if(!fuck&&num>=x)cache[++yls]=num/x,num%=x,fuck=true;
    }
    if(yls>ansp){ansp=yls;for(int i=1;i<=yls;i++)ans[yls-i+1]=cache[i];}
    else if(yls==ansp)
            for(int i=1;i<=yls;i++)
            {
                if(!flag)
                    if(ans[yls-i+1]>cache[i])break;
                    else if(ans[yls-i+1]<cache[i])flag=true;
                if(flag)ans[yls-i+1]=cache[i];  
            }
}
int main()
{
    scanf("%d",&n);n++;for(int i=1;i<=n;i++)scanf("%d%d",&a[i].l,&a[i].r);
    s[1]=1;pps=1;sort(a+2,a+1+n,cmp);cheng(a[1].l);
    for(int i=2;i<=n;i++)
    {chu(a[i].r);cheng(a[i].l);}
    for(int i=ansp;i;i--)printf("%d",ans[i]);
}

问题 U: Radar Installation

通过岛屿和圆的半径来得出放置仪器能够覆盖到岛屿的区间
按照左端点排序后按照贪心放置即可

#include<bits/stdc++.h>
using namespace std;
struct ss{double l,r;}a[10010];
bool cmp(ss x,ss y){return x.l<y.l;}
int n,ans,tot;double r,x,y,now;
int main()
{
    while(~scanf("%d%lf",&n,&r))
    {
        if(!n&&!r)break;bool flag=false;ans=0;now=-2e9;tot++;
        for(int i=1;i<=n;i++)
        {   
            scanf("%lf%lf",&x,&y);if(y>r)flag=true;
            a[i].l=x-sqrt(r*r-y*y);a[i].r=x+sqrt(r*r-y*y);
        }
        if(flag){printf("Case %d: -1\n",tot);continue;}sort(a+1,a+1+n,cmp);
        for(int i=1;i<=n;i++)
        {
            if(a[i].l>now)ans++,now=a[i].r;
            else now=min(now,a[i].r);
        }
        printf("Case %d: %d\n",tot,ans);
    }
}

问题 V: Stall Reservations

先按照区间左端点排序 注意记录序号
一个循环队列 挤完奶就把畜栏加入队列 只要队列里还有元素就开始工作

#include<bits/stdc++.h>
using namespace std;
struct ss{int l,r,x;}a[100000],b[100000];
bool cmp1(ss x,ss y){return x.l<y.l;}bool cmp2(ss x,ss y){return x.r<y.r;}
int n,now,anow,bnow,wei,Min,Max,s[1010000],ans,ch[1010000],nai[100000],dui[100000];
int main()
{
    scanf("%d",&n);Min=1e9;
    for(int i=1;i<=n;i++)scanf("%d%d",&a[i].l,&a[i].r),a[i].x=i,b[i].x=i,b[i].l=a[i].l,b[i].r=a[i].r,s[a[i].l]++,s[a[i].r+1]--,Max=max(Max,a[i].r),Min=min(Min,a[i].l);
    for(int i=1;i<=Max;i++)ch[i]=ch[i-1]+s[i],ans=max(ans,ch[i]);
    printf("%d\n",ans);sort(a+1,a+1+n,cmp1);sort(b+1,b+1+n,cmp2);for(int i=1;i<=ans;i++)dui[i]=i;now=1;wei=ans;anow=1;bnow=1;
    for(int i=Min;i<=Max;i++)
    {
        while(a[anow].l==i)nai[a[anow++].x]=dui[now++];
        while(b[bnow].r==i)dui[++wei]=nai[b[bnow++].x];
    }
    for(int i=1;i<=n;i++)printf("%d\n",nai[i]);
}

问题 W: Sunscreen

经典贪心 不过竟然不是按左端点排序 而是按右端点排序
这也说明了在贪心的时候线段的主要变量是右端点

#include<bits/stdc++.h>
using namespace std;
struct ss{int l,r;}a[10000];struct yy{int x,v;}b[10000];
bool cmp1(ss x,ss y){return x.r<y.r;}bool cmp2(yy x,yy y){return x.v<y.v;}
int n,m,ans;
int main()
{
    scanf("%d%d",&n,&m);for(int i=1;i<=n;i++)scanf("%d%d",&a[i].l,&a[i].r);for(int i=1;i<=m;i++)scanf("%d%d",&b[i].v,&b[i].x);
    sort(a+1,a+1+n,cmp1);sort(b+1,b+1+m,cmp2);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            if(a[i].l<=b[j].v&&b[j].v<=a[i].r&&b[j].x){ans++;b[j].x--;break;}
    printf("%d",ans);
}

问题 X: 占卜DIY

大模拟题 细心就好了
不过我在其他网站都交过了 本地OJ却没办法过
还是自己太菜了吧 也不知道有没有好心人能帮忙看看错误 RE

#include<bits/stdc++.h>
using namespace std;
int a[200][100],lis[200],num[200],now,life,ans;char c,pps;
int main()
{
	for(int i=1;i<=13;i++)
	{
		for(int j=1;j<=4;j++)
		{
			scanf("%c%c",&c,&pps);
			if(c=='0')a[i][j]=c-38;
			else if(c=='A')a[i][j]=c-64;
			else if(c=='J')a[i][j]=c-63;
			else if(c=='Q')a[i][j]=c-69;
			else if(c=='K')a[i][j]=c-62;
			else a[i][j]=c-48;
		}lis[i]=4;
	}
	now=13;life=4;lis[13]=1;
	while(life&&ans<12)
	{
		if(a[now][lis[now]]==13){num[a[now][lis[now]]]++;life--;if(now==13)lis[now]++;else lis[now]--;now=13;continue;}
		num[a[now][lis[now]]]++;if(num[a[now][lis[now]]]==4)ans++;int pps=now;now=a[now][lis[now]];if(pps==13)lis[pps]++;else lis[pps]--;
	}printf("%d\n",ans);
}

问题 Y: 防线

想法很巧妙 利用了奇偶性 使得能够二分寻找答案
只要保证check的左区间为偶数就在右区间找 否则就在左区间找

#include<bits/stdc++.h>
#define LL long long
using namespace std;
LL T,n,l,r,mid,ans;
struct ss{LL s,e,d;}a[200100];
LL work(LL x,LL y){if(x%y)return 1;else return 0;}
bool check1(LL x)
{
    ans=0;
    for(LL i=1;i<=n;i++)
    {
        if(x>=a[i].s){LL pps=min(a[i].e,x);ans+=(pps-a[i].s)/a[i].d+1;}
    }if(ans%2)return true;else return false;
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);l=1;r=0;
        for(LL i=1;i<=n;i++){scanf("%d%d%d",&a[i].s,&a[i].e,&a[i].d);r=max(r,a[i].e);}r++;
        while(l<r){mid=(l+r)/2;if(check1(mid))r=mid;else l=mid+1;}ans=0;
        for(LL i=1;i<=n;i++)if(l>=a[i].s&&(l-a[i].s)%a[i].d==0)ans++;
        if(ans%2)printf("%d %d\n",l,ans);else printf("There's no weakness.\n");
    }
}

问题 AD: To the Max

不要问我为什么一下跳这么远 咕了就是咕了 还要别人不问
就是一个二维的最大连续子列 枚举矩阵的行数 然后将前缀和的差值作为一维的最大连续子列的权值
剩下的就和一维的最大连续子列一样

#include<bits/stdc++.h>
using namespace std;
int n,ans,a,sum[110][110],dp[110];
int main()
{
	while(~scanf("%d",&n))
	{
		for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)scanf("%d",&a),sum[i][j]=sum[i-1][j]+a;ans=0;
		for(int i=1;i<=n;i++)
			for(int j=i;j<=n;j++)
			{
				for(int k=1;k<=n;k++){dp[k]=max(sum[j][k]-sum[j-1][k],dp[k-1]+sum[j][k]-sum[j-i][k]);if(dp[k]<0)dp[k]=0;}
				for(int k=1;k<=n;k++)ans=max(ans,dp[k]);memset(dp,0,sizeof(dp));
			}
		printf("%d\n",ans);memset(sum,0,sizeof(sum));
	}
}

其实我还是想补完的 无可奈何自己还是太菜了 没办法 先咕着吧

posted @ 2020-09-25 20:58  qwaszxxyf  阅读(113)  评论(0编辑  收藏  举报