【做题记录】构造题

CF468C Hack it!

题意:

\(F(x)\) 表示 \(x\) 的各个位上的数字之和,如 \(F(1234)=1+2+3+4=10\)

给定 \(a(a\le 10^{18})\) ,请求出任意一组 \(l,r(l,r\le 10^{200})\) ,要求满足:

\[\sum_{i=l}^{r}F(i)\pmod{a}=0 \]

输出 \(l,r\)

$\texttt{solution}$

注意到,若 \(F(x)=p\) ,那么 \(F(x+10^{18})=F(x)+1=p+1\)

那么可以发现,若 \(\sum_{i=0}^{10^{18}-1}F(i)=p\) ,那么有:

\[\sum_{i=1}^{10^{18}}F(i)=sum_{i=1}^{10^{18}-1}+F(10^{18})-F(0)=p+1 \]

因此发现 \(l=a-p,r=a-p+10^{18}-1\) 时恰好能够成立。

因此考虑求出 \(p\)

\[\begin{aligned}\sum_{i=0}^{10^{18}-1}&=45\times 10^{17}+10\times \sum_{i=0}^{10^{17}-1}f(i)\\&=45\times 10^{17}+10\times (45\times 10^{16})+100\times \sum_{i=0}^{10^{16}-1}f(i)\\&=\dots\\&=18\times 45\times 10^{17}\\&=81\times 10^{18}\end{aligned} \]

之后带入式子就可以啦!

typedef unsigned long long ll;
ll a,l,r,p,inf=1e18;
int main()
{
	 a=rd(),p=inf%a*9llu%a*9llu%a;
	 printf("%llu %llu\n",a-p,a-p+inf-1llu);
     return 0;
}

CF1491F Magnets

交互、二分。

早苗有 \(n\) 块磁石,编号为 \(1,2,\cdots,n\)。每块磁石的磁极可能是正极,负极,也可能没有磁性。她希望你能帮她找出所有没有磁性的磁石。

万幸的是,你有一台磁力检测仪。你每次可以将每个磁石放在这台机器的左托盘,右托盘或者不放。

机器将会返回此时的磁力强度。设托盘左边有 \(n_1\) 个磁石为正极,\(s_1\) 个磁石为负极,托盘右边中有 \(n_2\) 磁石为正极,\(s_2\) 个磁石为负极,则返回的磁力强度为 \(n_1n_2+s_1s_2-n_1s_2-n_2s_1\)

如果一次测试中磁力强度的绝对值大于 \(n\),这台机器就会坏掉。

你需要在 \(n+\lfloor\log_2n\rfloor\) 次测试内找到所有没有磁性的磁石的编号,同时不能弄坏机器。

保证存在至少 \(2\) 块磁石有磁性且至少 \(1\) 块磁石没有磁性。

$\texttt{solution}$

先化简式子发现交互的返回值就是 \((n_1-s_1)(n_2-s_2)\)

由于正负极石头放在一起会导致 \(n,s\) 会都大于 \(0\) ,使问题变得更为困难。

那么考虑每次查询只对一块石头与其他一堆石头之间进行询问。

那么如果我们已经知道了一块有磁性的此时,就可以非常容易的知道其他所有的此时是否有磁性。

之后考虑如何才能找出有磁性的石头,直接枚举肯定是不行的,最坏都会到 \(O(n^2)\)

我们可以从 \(1\)\(n\) 开始枚举 \(i\),询问 \([1,i-1]\)\(i\)。若询问结果不为 \(0\),则 \([1,i-1]\) 中必然有一块有磁性的石头,而 \(i\) 也一定是有磁性的。因此可以找出一块有磁性的石头。

之后我们是否可以 \(O(n)\) 检查所有石头了呢?还是不行。。。

考虑到答案不能超过 \(n+\log n\),所以我们只能将上面第二块石头之后,也就是 \([i+1,n]\) 中的石头判断一遍。这样到现在为止总共用了 \(n-1\) 次操作。

\([1,i-1]\) 中只有 \(1\) 快有磁性的石头,所以我们可以二分出这块石头的位置,找出这最后一块有磁性的石头。那么我们就做完啦。

int T,n,Last,pos,cnt;
int ans[Maxn];
inline int query(int nl,int nr,int k)
{
	 printf("? %d %d\n",nr-nl+1,1);
	 for(int i=nl;i<=nr;i++) printf("%d%c",i,(i==nr)?'\n':' ');
	 printf("%d\n",k);
	 fflush(stdout);
	 return rd();
}
inline void print()
{
	 printf("! %d ",cnt);
	 for(int i=1;i<=cnt;i++) printf("%d%c",ans[i],(i==cnt)?'\n':' ');
	 fflush(stdout);
}
int main()
{
	 T=rd();
	 while(T--)
	 {
	 	 n=rd(),pos=-1,cnt=0;
	 	 for(int i=2;i<=n && pos==-1;i++)
	 	 	 if(query(1,i-1,i)) pos=i;
	 	 for(int i=pos+1;i<=n;i++) if(!query(i,i,pos)) ans[++cnt]=i;
	 	 int nl=1,nr=pos-1;
	 	 while(nl<=nr)
	 	 {
	 	 	 int mid=(nl+nr)>>1;
	 	 	 if(query(1,mid,pos)) Last=mid,nr=mid-1;
	 	 	 else nl=mid+1;
		 }
		 for(int i=1;i<pos;i++) if(i!=Last) ans[++cnt]=i;
		 print();
	 }
	 return 0;
}

CF1586F Defender of Childhood Dreams

给定一张竞赛图(点数 \(\le 1000\)),对于所有 \(a<b\),都有一条由 \(a\)\(b\) 的有向边,并且每一条边都有一个颜色。现在要求所有长度大于等于 \(k\) 的路径上都有 \(\ge 2\) 中颜色。求出整张图中出现最少出现颜色的数量与边的染色方案。

$\texttt{solution}$

考虑将序列分为许多长度不超过 \(k\) 个块,在块与块间连接相同颜色的边。这样可以保证在块与块间转移的边不会形成 \(\ge k\) 长度的路径。

在每一个块内部再进行同样的拆分(但在块内的颜色要与块外的颜色不同),递归进行即可。

#define Maxn 1005
int n,k,ans;
int col[Maxn][Maxn];
void solve(int l,int r,int c)
{
	 if(l==r) return;
	 int len=(r-l+k)/k,tot=(r-l+len)/len;
	 for(int i=1,x,y;i<tot;i++)
	 	 for(int j=i+1;j<=tot;j++)
	 	 	 for(int p=1;p<=len;p++)
	 	 	 	 for(int q=1;q<=len;q++)
	 	 	 	 {
	 	 	 	 	 x=l+(i-1)*len+p-1,y=l+(j-1)*len+q-1;
	 	 	 	 	 if(y>r) break;
	 	 	 	 	 col[x][y]=c;
				 }
	 for(int i=1;i<=tot;i++) solve(l+(i-1)*len,min(l+i*len-1,r),c+1);
} 
int main()
{
	 n=rd(),k=rd();
	 solve(1,n,1);
	 for(int i=1;i<n;i++) for(int j=i+1;j<=n;j++) ans=max(ans,col[i][j]);
	 printf("%d\n",ans);
	 for(int i=1;i<n;i++) for(int j=i+1;j<=n;j++) printf("%d ",col[i][j]);
	 printf("\n");
	 return 0;
}

CF715D Create a Maze

有一个 \(n\times m\) 的迷宫,每一格都是一个房间,每两个相邻的房间之间有一扇门。

在所有门中,有 \(k\) 扇是锁着的,不能通行,其余没有限制。

现在你在 \((1,1)\),需要走到 \((n,m)\),只能向下或向右走。

设总共的行走方案有 \(T\) 种。

现在给出 \(T\),要求设计出一个迷宫满足行走方案为 \(T\)

要求:\(n,m\le 50,k\le 500,T\le 10^{18}\)

$\texttt{solution}$

这一题需要按照 \(T\) 的进制来解决问题。

首先考虑用二进制,那么我们可以这样设计方案:

这样我们就可以用二进制来表示出任何 \(\le 2^{49}\)\(T\) 啦!

然而我们发现如果我们将我们的以 \(2\times 2\) 改为 \(3\times 3\),可以将前面的二进制变为六进制!!

之后构造就比较类似,我们只要改为两路 \(1\) 和中间的 \(3\times 3\) 即可。

这样最大可以表示 \(6^{24}>10^{18}\),可以解决这道题啦!

posted @ 2021-10-08 12:06  EricQian06  阅读(40)  评论(1编辑  收藏  举报