T0 谜之阶乘
题意:给定一个数 \(x\),\(x\) 为 \(\dfrac{a!}{b!}\),求 \(a\) 和 \(b\)。(多测,\(1\le x\le 10^{18}\))
阶乘增长很快,\(a\) 和 \(b\) 的范围很小,所以直接暴力枚举即可。
T1 小P的2048
点击查看题目
点击查看代码
#include<bits/stdc++.h>
#define int long long
inline int read(){
char ch=getchar();int x=0,f=1;
for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);
return x*f;
}
const int N=10;
int last[N][N],now[N][N],ans,sum,n,m;
bool vis[N][N];
inline void up(){
for(int i=2;i<=n;++i){
for(int j=1;j<=n;++j){
if(now[i][j]){
int x=i,y=j;
while(x>1){
if(now[x-1][y]){
if(now[x-1][y]==now[x][y]){
if(!vis[x-1][y]){
vis[x-1][y]=1;
now[x-1][y]*=2;
sum+=now[x-1][y];
now[x][y]=0;
}
}
break;
}else{
now[x-1][y]=now[x][y];
now[x][y]=0;
x--;
}
}
}
}
}
}
inline void down(){
for(int i=n-1;i;--i){
for(int j=1;j<=n;++j){
if(now[i][j]){
int x=i,y=j;
while(x<n){
if(now[x+1][y]){
if(now[x+1][y]==now[x][y]){
if(!vis[x+1][y]){
vis[x+1][y]=1;
now[x+1][y]*=2;
sum+=now[x+1][y];
now[x][y]=0;
}
}
break;
}else{
now[x+1][y]=now[x][y];
now[x][y]=0;
x++;
}
}
}
}
}
}
inline void left(){
for(int j=2;j<=n;++j){
for(int i=1;i<=n;++i){
if(now[i][j]){
int x=i,y=j;
while(y>1){
if(now[x][y-1]){
if(now[x][y-1]==now[x][y]){
if(!vis[x][y-1]){
vis[x][y-1]=1;
now[x][y-1]*=2;
sum+=now[x][y-1];
now[x][y]=0;
}
}
break;
}else{
now[x][y-1]=now[x][y];
now[x][y]=0;
y--;
}
}
}
}
}
}
inline void right(){
for(int j=n-1;j;--j){
for(int i=1;i<=n;++i){
if(now[i][j]){
int x=i,y=j;
while(y<n){
if(now[x][y+1]){
if(now[x][y+1]==now[x][y]){
if(!vis[x][y+1]){
vis[x][y+1]=1;
now[x][y+1]*=2;
sum+=now[x][y+1];
now[x][y]=0;
}
}
break;
}else{
now[x][y+1]=now[x][y];
now[x][y]=0;
y++;
}
}
}
}
}
}
inline void add(int k,int v){
int tot=0,zc=0;
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
if(!now[i][j])tot++;
tot=1+(k%tot);
for(int i=1;i<=n;++i){
bool pd=0;
for(int j=1;j<=n;++j){
if(!now[i][j])zc++;
if(zc==tot){now[i][j]=v;pd=1;break;}
}
if(pd)break;
}
}
inline void check(){
ans++;
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
if(last[i][j]!=now[i][j])return;
ans--;
std::cout<<ans<<'\n'<<sum<<'\n';exit(0);
}
signed main(){
// freopen("in.in","r",stdin),freopen("out.out","w",stdout);
std::ios::sync_with_stdio(false);std::cin.tie(0),std::cout.tie(0);
n=read(),m=read();
int x1=read(),y1=read(),v1=read(),x2=read(),y2=read(),v2=read();
last[x1][y1]=v1,last[x2][y2]=v2;now[x1][y1]=v1,now[x2][y2]=v2;
for(int i=1;i<=m;++i){
memset(vis,0,sizeof(vis));
int d=read(),k=read(),v=read();
if(d==0)up();
if(d==1)down();
if(d==2)left();
if(d==3)right();
check();
add(k,v);
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
last[i][j]=now[i][j];
}
std::cout<<ans<<'\n'<<sum<<'\n';
}
T2 子集
点击查看题目
My Algorithm:
将 \(n\) 个数平均分为 \(k\) 组,每组有 \(len=\dfrac{n}{k}\) 个数,将 \(k\) 个数划分为一组。只需要在这 \(len\) 组数中每组选一个即可构造出一组答案。
关于 \(k\) 和 \(len\),有如下情况:
- \(k\) 为奇数,\(len\) 为奇数。
- \(k\) 为奇数,\(len\) 为偶数。
- \(k\) 为偶数,\(len\) 为偶数。
至于为什么没有 \(k\) 为偶数,\(len\) 为奇数。这种情况,证明如下:
当 \(k\) 为偶数,\(len\) 为奇数。时,显然有 \(sum=\dfrac{n\cdot (n+1)}{2}\) 为奇数,此时 \(k\nmid sum\),无解。
偶数随便构造,下面介绍奇数的构造。
奇数的构造比较困难在于 \(len\) 为奇数,如果按照偶数的方式来构造的话,就会剩下一组,并且所有组都要用到剩下这一组中间的数,可以先构造出公差为 \(1\) 的一些数,然后直接再按照偶数构造的方式构造 \(n-2\) 列,这样他们的差也是 \(1\),这么说不好理解,附图如下。
n=15 k=3
发现红色区域的构造和偶数如出一辙,但由于 \(len\) 为奇数,所以他们的差为 \(1\),所以继续构造出 \(3\) 组数,每组有 \(2\) 个数,且他们的和差值为 \(1\)。
前面容易处理,考虑后面,经过观察奇数和奇数,偶数和偶数,发现他们之间都相差 \(2\),所以构造出为在两段中的差值都为 \(1\)。
发现奇数对应前一段,偶数对应后一段,如 n=25 k=5
这样的情况。
相信你已经看出规律了。代码如下。
点击查看代码
#include<bits/stdc++.h>
#define int long long
inline int read(){
char ch=getchar();int x=0,f=1;
for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);
return x*f;
}
signed main(){
// freopen("in.in","r",stdin),freopen("out.out","w",stdout);
std::ios::sync_with_stdio(false);std::cin.tie(0),std::cout.tie(0);
int T=read();
while(T--){
int n=read(),k=read();
int len=n/k,s=n*(n+1)/2;
if(s%k||(k==n&&n!=1)){std::cout<<"No\n";continue;}
std::cout<<"Yes\n";
if(k==1){
for(int i=1;i<=n;++i)std::cout<<i<<' ';
std::cout<<'\n';continue;
}
if(len&1){
int qian=len/2;
for(int i=1;i<=k;++i){
int sum=0;
for(int j=i;j<=qian*k;j+=k){
std::cout<<j<<' ';sum+=j;
}
int w=len-2;
for(int j=w*k-i+1;j>qian*k;j-=k){
std::cout<<j<<' ';sum+=j;
}
sum=s/k-sum;
if(i&1){
int zc=n-(i+1)/2+1;
std::cout<<sum-zc<<' '<<zc;
}else{
int zc=len/2*2*k-(i/2)+1;
std::cout<<zc<<' '<<sum-zc;
}
std::cout<<'\n';
}
}else{
for(int i=1;i<=k;++i){
for(int j=i;j<=n/2;j+=k)std::cout<<j<<' ';
for(int j=n-i+1;j>n/2;j-=k)std::cout<<j<<' ';
std::cout<<'\n';
}
}
}
}
T3 混凝土粉末
点击查看题目
无脑做法为:主席树加二分,空间会炸,常数会炸,和优秀暴力一个分。
点击查看代码
#include<bits/stdc++.h>
#define int long long
inline int read(){
char ch=getchar();int x=0,f=1;
for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);
return x*f;
}
const int N=1e6+10;
int n,m,cnt,root[N],a[N],now;
struct Tree{int ls,rs,h,tag;}t[N<<4];
inline void modify(int last,int p,int x,int y,int d,int l,int r){
t[p]=t[last];
if(x<=l&&r<=y){t[p].tag=t[p].tag+d;return;}
int mid=l+r>>1;
if(x<=mid)modify(t[last].ls,t[p].ls=++cnt,x,y,d,l,mid);
if(y>mid)modify(t[last].rs,t[p].rs=++cnt,x,y,d,mid+1,r);
}
inline int query(int p,int x,int l,int r){
if(l==r){return t[p].h+t[p].tag;}
int mid=l+r>>1;
if(x<=mid)return query(t[p].ls,x,l,mid)+t[p].tag;
else return query(t[p].rs,x,mid+1,r)+t[p].tag;
}
inline int work(int x,int y,int r){
if(query(root[r],x,1,n)<y)return 0;
int ans=0,l=1;
while(l<=r){
int mid=l+r>>1;
if(query(root[mid],x,1,n)>=y)r=mid-1,ans=mid;
else l=mid+1;
}
return ans;
}
signed main(){
// freopen("in.in","r",stdin),freopen("out.out","w",stdout);
std::ios::sync_with_stdio(false);std::cin.tie(0),std::cout.tie(0);
n=read(),m=read();
for(int i=1;i<=m;++i){
root[i]=root[i-1];
int opt=read();
if(opt==1){
int l=read(),r=read(),h=read();
modify(root[i-1],root[i]=++cnt,l,r,h,1,n);
}else{
int x=read(),y=read();
std::cout<<work(x,y,i)<<'\n';
}
}
}
正解做法为:把操作离线下来,扫描 \(1\) 到 \(n\) 的位置,用下标为操作时间的树状数组维护每个位置的所有操作时间的信息。
具体如下:扫描到一个位置 \(i\),如果他是左端点,在树状数组上区间加 \(d\),是右端点就区间减 \(d\),消除这次操作的影响。此时树状数组维护的就是位置 \(i\) 在所有时刻的信息。如果是询问的话就在树状数组上倍增,找到第一个 \(h_i\ge y\) 的时间。
点击查看代码
#include<bits/stdc++.h>
#define int long long
inline int read(){
char ch=getchar();int x=0,f=1;
for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);
return x*f;
}
const int N=1e6+10;
int n,q;
std::vector<std::pair<int,int>> st[N],qu[N];
struct Tree{
int c[N];
inline void add(int x,int d){for(int i=x;i<=q;i+=i&-i)c[i]+=d;}
inline int query(int x,int y){
int l=0,t=0;
for(int i=std::__lg(x);i>=0;--i)
if((l|1<<i)<=x&&t+c[(l|1<<i)|l]<y)
t+=c[l|1<<i],l+=1<<i;
return l==x?0:l+1;
}
}t;
int __[N];
bool vis[N];
signed main(){
// freopen("in.in","r",stdin),freopen("out.out","w",stdout);
std::ios::sync_with_stdio(false);std::cin.tie(0),std::cout.tie(0);
n=read(),q=read();
for(int i=1;i<=q;++i){
int opt=read();
if(opt==1){
int l=read(),r=read(),h=read();
st[l].push_back({i,h});
st[r+1].push_back({i,-h});
}else{
int x=read(),y=read();
qu[x].push_back({i,y});
vis[i]=true;
}
}
for(int i=1;i<=n;++i){
for(auto it:st[i])t.add(it.first,it.second);
for(auto it:qu[i])__[it.first]=t.query(it.first,it.second);
}
for(int i=1;i<=q;++i)if(vis[i])std::cout<<__[i]<<'\n';
}
大多数人的做法为:正解,但是没有在树状数组上二分,而是二分套树状数组。
这是白简的代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <vector>
#define int long long
using namespace std;
const int N = 2005000;
int n,q;
int opt[N],res[N];
vector< pair<int,int> > g[N],h[N];
class BIT{
private:
int bit[N];
int lowbit(int x) {
return x & (-x);
}
public:
void Add(int x,int y,int val) {
for(int i = x;i <= y; i += lowbit(i))
bit[i] += val;
}
int Query(int x) {
int ans = 0;
for(int i = x;i; i -= lowbit(i))
ans += bit[i];
return ans;
}
}tree;
signed main() {
cin >> n >> q;
for(int i = 1,l,r,x,y;i <= q; i++) {
cin >> opt[i];
if(opt[i] == 1) {
cin >> l >> r >> x;
g[l].push_back(make_pair(i,x));
g[r + 1].push_back(make_pair(i,-x));
}
else {
cin >> x >> y;
h[x].push_back(make_pair(i,y));
}
}
for(int i = 1;i <= n; i++) {
for(int j = 0;j < g[i].size(); j++)
tree.Add(g[i][j].first,q,g[i][j].second);
for(int j = 0;j < h[i].size(); j++) {
int l = 1,r = h[i][j].first,mid;
while(l < r) {
mid = (l + r) >> 1;
if(tree.Query(mid) >= h[i][j].second)
res[h[i][j].first] = r = mid;
else
l = mid + 1;
}
}
}
for(int i = 1;i <= q; i++)
if(opt[i] == 2)
cout << res[i] << endl;
return 0;
}
不知道为啥这个双 log 有人写的跑的比正解快,技不如人,膜拜~~
这里有一个赛时nishishui123的高分暴力,和主席树同分
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e6+100;
int n,q,p,num;
ll x,y;
struct node{
int l,r,id;
ll h;
}name[N];
ll height;
int main()
{
scanf("%d%d",&n,&q);
for(int t=1;t<=q;t++){
scanf("%d",&p);
if(p==1){
++num;
scanf("%d%d%lld",&name[num].l,&name[num].r,&name[num].h);
name[num].id=t;
}
else{
scanf("%lld%lld",&x,&y);
height=0;
bool flag=0;
for(int i=1;i<=num;i++){
if(name[i].l<=x&&x<=name[i].r){
height+=name[i].h;
if(height>=y){
flag=1;
printf("%d\n",name[i].id);
break;
}
}
}
if(flag==0)puts("0");
}
}
}
T4 排水系统
点击查看题目
比较板的概率期望,该润了,明天补。
标签:ch,奇数,int,long,2024,include,now,初三,集训 From: https://www.cnblogs.com/Ishar-zdl/p/18024163