1012练习赛
T1 果果系统 NKOJ8690
有一个n*m的网络,一个文件图标占用2*2的正方形空间,必须放在整格点之内,且不能超出桌面。 GOS2021 (果果操作系统(Guo Operating System,GOS)近期推出了2021船新版本)引入了一些船新忒星,例如允许图标重叠。 但是根据规定,如果一个格子存在多个重叠的图标,只会显示最后一个。 每个图标不可以被遮挡一半以上,而你想在GOS中放入尽可能多的图标。 问最大能放多少个,以及方案。 数据范围: 1<=n,m<=500
这道题稍微推一推就可以分析出方法,kzsn在这里主要说一下对于这种构造题需要注意的东西:
1.一定要看一下奇偶性,很有可能奇偶性不同会导致不同的结果!
2.不要局限于小样例或者自己造的小数据,小数据不能有些性质!
#include<bits/stdc++.h> using namespace std; #define re register int #define py pair<int,int> vector<py>G; signed main() { int n, m; scanf("%d%d",&n,&m); if(n<2 || m<2) return puts("0"), 0; if(n % 2 == 0) { for(re i=1, z=0;i<=n-1;i+=2) { z^=1; if(z) { for(re j=1;j<=m-1;++j) G.push_back(py(i, j)); if(i<=n-2)G.push_back(py(i+1, m-1)); } else { for(re j=m-1;j;--j) G.push_back(py(i, j)); if(i<=n-2)G.push_back(py(i+1, 1)); } } } else if(m % 2 == 0) { for(re i=1, z=0;i<=m-1;i+=2) { z^=1; if(z) { for(re j=1;j<=n-1;++j) G.push_back(py(j, i)); if(i<=m-2)G.push_back(py(n-1, i+1)); } else { for(re j=n-1;j;--j) G.push_back(py(j, i)); if(i<=m-2)G.push_back(py(1, i+1)); } } } else { for(re i=1;i<=m-1;i+=2)G.push_back(py(n-1, i)); for(re i=1, z=0;i<=n-1;i+=2) { z^=1; if(z) { for(re j=1;j<=m-1;++j) G.push_back(py(i, j)); if(i<n-2)G.push_back(py(i+1, m-1)); } else { for(re j=m-1;j;--j) G.push_back(py(i, j)); if(i<n-2)G.push_back(py(i+1, 1)); } } } printf("%d\n", (int)G.size()); for(py i:G)printf("%d %d\n", i.first, i.second); return 0; }
T2 质数幂 NKOJ8691
对于一个n>1的正整数n,对它进行质因数分解会得到一系列质因数 pi ,以及指数 ri 。求 min {r1,r2,r3,...,rk}。 多组数据。 数据范围: 1<=n<=1^18, 1<=T<=50000.
我们先想一下,如果不管多组数据,我们就单独求 n 的质因数分解,也不可能按照根号分解。
既然一个根号不行,那么我们试一下开立方根!
我们预处理处 1 ~ 100000 的质因数, 然后把很多个质因数分解 n ,分解完后 n 很有可能还剩余了很多质因数没被分解。
但是,那些质因数都是 大于 100000 的质因数, 所以只有可能是 质因数 x,y,
x2,x3,x4,(xy)2,只有这几种情况能让答案不为1。
至于怎么得到x和y呢,用系统函数pow即可大致得到(似乎有精度误差,所以我给它加减了个2和1)
但是这样搞的话,有多组数据,咋办啊!
由于我们耗得时间主要是在 1 ~ 100000 的质因数的个数,所以我们尝试将质因数个数减少。
减少到大约 6000 , 这样呢,复杂度就能过,质因数个数为 783 ,时间复杂度为O(783*T)
减少到 6000 后,我们分析也会变,但可以发现 6000 以上的质因数,它最多还是 x4,所以可过!
对了,可能有同学不理解那个(xy)2,这个东西能贡献的答案还是2,我们不需要将x,y分别求出来,只需要知道 n 能被开方就行了!
#include<bits/stdc++.h> using namespace std; #define re register int #define int long long const int N=1e6+6, INF=1e18+1; int lsp[N], prm[N], cnt; inline void euler_phi(int x) { for(re i=2;i<=x;++i){ if(!lsp[i])prm[++cnt]=i; for(re j=1;j<=cnt && prm[j]*i<=x;++j) { lsp[prm[j]*i]=1; if(i%prm[j]==0)break; } } } inline int ksm(int x, int y){ int ret=1; while(y--)ret=ret*x; return ret; } signed main() { euler_phi(6000); int T, n; scanf("%lld",&T); while(T--) { scanf("%lld",&n); int ret=100000; for(re i=1;i<=cnt && ret!=1 && prm[i]<=n;++i) if(n%prm[i]==0) { int tmp=0, t=prm[i]; while(n%t==0) tmp++,n/=t; ret=min(ret,tmp); } if(n>1 && ret!=1) { for(re i=5;i>=1;--i) { if(i==1){ret=1;break;} int t=pow(1.0*n, 1.0/i), flag=0; if(t<6000)continue; for(re j=-2;j<=2;++j) if(ksm(t+j, i)==n) { flag=1; break; } if(flag) { ret=min(ret, i); break; } } } printf("%lld\n", ret); } return 0; }
T3 又甘又刻 NKOJ8692
MikeZ同时在玩 n 款毒瘤手游,它们有的很肝,有的很氪,有的很凹。 MikeZ对于每个游戏都评价了三个值,肝度 a ,氪度 b,凹度 c。 定义一个游戏的相对可玩性为: P = max{max(0, ai-a) + max(0, bi-b) + max(0, ci-c)}。 其中 i = 1 ~ n。 请你帮助 MikeZ 计算出相对可玩性最低的游戏。 若有多款游戏满足要求,选择编号最小的游戏 (编号小代表入坑早,凉得也更早)。
数据范围:
1<=n<=10^6, 1<=ai,bi,ci<=10^9
这道题很巧妙的运用了max(0, x)的性质,也就是它不可能小于0!
我们想一种方法,比如说想把所有手游的 a,b,c 加起来,在相减得到的可玩度,肯定会小于等于真正的可玩度。
因为他的max(0),所以让权值和直接减会排除一些不太优秀的选择。
那么我们试一试把其中可能全部求一个最大值,也就是 $a,b,c,ab,ac,bc,abc$。
求出七个最大值之后呢,我们求第i个手游的可玩度时,可以直接得到是七种差值的最大值!
至于为什么呢,因为第i个手游的真正可玩度肯定是七种当中的一种,
而且由于max(0),所以真正的可玩度肯定是其中的最大值!
#include<bits/stdc++.h> using namespace std; #define re register int #define LL long long const int N=1e6+6; LL t1, t2, t3, t4, t5, t6, t7, p1, p2, p3, p4, p5, p6, p7, tmp=1e18, a[N], b[N], c[N], val; int n, ret; signed main() { scanf("%d",&n); for(re i=1;i<=n;++i) { scanf("%lld%lld%lld",&a[i],&b[i],&c[i]); t1=max(t1, a[i]); t2=max(t2, b[i]); t3=max(t3, c[i]); t4=max(t4, a[i]+b[i]); t5=max(t5, a[i]+c[i]); t6=max(t6, b[i]+c[i]); t7=max(t7, a[i]+b[i]+c[i]); } for(re i=1;i<=n;++i) { p1=t1-a[i]; p2=t2-b[i]; p3=t3-c[i]; p4=t4-a[i]-b[i]; p5=t5-a[i]-c[i]; p6=t6-b[i]-c[i]; p7=t7-a[i]-b[i]-c[i]; val=max(p1, max(p2, max(p3, max(p4, max(p5, max(p6, p7)))))); if(val<tmp)ret=i,tmp=val; } printf("%d %lld\n", ret, tmp); return 0; }
T4 区间计数 NKOJ8693
对于序列 a1,a2,a3,...,an,求满足下列所有条件的区间 [l,r] 的个数。 1<=l<=r<=n; 区间内的数字各不相同 max{al,al+1,...,ar} - (r-l+1) <= k 数据范围: 1<=n,k<=3*10^5, 1<=ai<=n
可以很明显是笛卡尔树题!
因为区间内数字各不相同,所以我们可以 O(n) 求出对于 i 号点的左右边界!
然后就用笛卡尔树,找到一个数可以充当最大值的区间,就成笛卡尔树的板题了!
#include<bits/stdc++.h> using namespace std; #define re register int #define LL long long const int N=3e5+5; int n, K; LL ans; int a[N], lt[N], rt[N], lst[N], Q[N]; int root, ls[N], rs[N], sz[N]; inline void DFS(int x) { sz[x]=1; if(ls[x])DFS(ls[x]), sz[x]+=sz[ls[x]]; if(rs[x])DFS(rs[x]), sz[x]+=sz[rs[x]]; int ll=max(x-sz[ls[x]], lt[x]), rr=min(x+sz[rs[x]], rt[x]); int p=x-ll, q=rr-x; if(p<q) { for(re i=0;i<=p;++i) { int v=x-i; int fr=min(rt[v], rr); int to=x+(a[x]-K-1-i); to=max(to, x); if(fr-to+1>=1)ans+=fr-to+1; } } else { for(re i=0;i<=q;++i) { int v=x+i; int fr=max(lt[v], ll); int to=x-(a[x]-K-1-i); to=min(to, x); ans+=max(0,to-fr+1); } } } signed main() { scanf("%d%d",&n,&K); for(re i=1, h=0;i<=n;++i) { scanf("%d",&a[i]); while(h && a[Q[h]]<=a[i])ls[i]=Q[h--]; if(!h)root=i; else rs[Q[h]]=i; Q[++h]=i; } for(re i=1,fw=1;i<=n;++i) { lt[i]=fw; if(lst[a[i]])lt[i]=max(lt[i],lst[a[i]]+1); lst[a[i]]=i; fw=lt[i]; } memset(lst, 0, sizeof lst); for(re i=n, fw=n;i;--i) { rt[i]=fw; if(lst[a[i]])rt[i]=min(rt[i], lst[a[i]]-1); lst[a[i]]=i; fw=rt[i]; } DFS(root); printf("%lld\n", ans); return 0; }