CodeForces:1103(div1)&1104(div2)

前言

1104AB,1103A是水题
1103B是不错的交互题
C小清新构造
D毒瘤优化状压
E是恶心数学题(直接弃疗

CF1104A Splitting into digits

Description \text{Description} Description

给定一个数字 n n n ,请将它拆分成 a 1 + a 2 + ⋯ + a m , a i ∈ [ 1 , 9 ] a_1+a_2+\cdots + a_m,a_i \in [1,9] a1+a2++am,ai[1,9],使得 ∑ i = 1 m a i = n \sum_{i=1}^ma_i = n i=1mai=n,并且使 a i a_i ai 中不同的数尽量少。

输出方案,两行,第一行为数的个数 m m m,第二行为 m m m个数,由空格隔开,分别为 a 1 − a m a_1 - a_m a1am

Solution \text{Solution} Solution

智障题。输出 n n n 1 1 1 即可…

Description \text{Description} Description

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e6+100;
inline ll read(){
	ll x(0),f(1);char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
int n;
int main(){
	n=read();
	for(int i=1;i<=9;i++){
		if(n%i==0){
			printf("%d\n",n/i);
			while(n) printf("%d ",i),n-=i;
			return 0;
		}
	}
	printf("%d\n",(n+8)/9);
	while(n>9) printf("9 ");n-=9;
	printf("%d",n);
}

CF1104B Game with string

Description \text{Description} Description

A \text{A} A B \text{B} B 正在玩一个关于由小写拉丁字符构成的字符串 s s s 的游戏。

每一个人会轮流操作,先 A \text{A} A B \text{B} B

对于每一次操作,操作者需要将 s s s 中的两个 连续且相同 的字符消除,消除后的字符串由另一个人操作。

对于每一次操作,如果不能找到两个符合要求的字符,那么操作者输。

Solution \text{Solution} Solution

水题。
显然博弈论只是层皮,胜负就与操作次数的奇偶性有关。
由于有删除操作,我写了个链表,每次删除后再递归往两侧看能不能接着删。
看其他题解许多使用了栈,做法也很简单。

Code \text{Code} Code

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e5+100;
inline ll read(){
	ll x(0),f(1);char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
int n;
int l[N],r[N],ans;
char s[N];
bool vis[N];
void del(int x,int y){
	++ans;vis[x]=vis[y]=1;
	int xx=l[x],yy=r[y];
	r[xx]=yy;l[yy]=xx;
	if(xx>0&&yy<=n&&s[xx]==s[yy]) del(xx,yy);
	return;
}
int main(){
	scanf(" %s",s+1);n=strlen(s+1);
	for(int i=1;i<=n;i++) l[i]=i-1,r[i]=i+1;
	r[0]=1;l[n+1]=n;
	for(int i=1;i<=n;i++){
		if(vis[i]) continue;
		if(s[i]==s[l[i]]) del(l[i],i);
	}
	if(ans&1) printf("Yes");
	else printf("No");
}

CF1103A Grid game

Description \text{Description} Description

你有一个 4 × 4 4\times4 4×4 的棋盘和一些 1 × 2 1\times2 1×2 大小的方格,给你放入的顺序, 0 0 0 表示竖着放, 1 1 1 表示横着放,每当一行或一列全放满时会将这一行或列上的消除,输出任意一种使小方格不重叠的方案。

Solution \text{Solution} Solution

似乎和其他题解的方法不太一样。
竖着的先放 ( 1 , 1 ) (1,1) (1,1),再放 ( 3 , 1 ) (3,1) (3,1) 把上一个消掉。
横着的先放 ( 4 , 3 ) (4,3) (4,3),再放 ( 4 , 1 ) (4,1) (4,1) 把上一个消掉。
永远不会有冲突。

Code \text{Code} Code

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e5+100;
inline ll read(){
	ll x(0),f(1);char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
int n;
int a,b;
char s[N];
int main(){
	scanf(" %s",s+1);n=strlen(s+1);
	for(int i=1;i<=n;i++){
		if(s[i]=='0'){
			if(a) printf("3 1\n");
			else printf("1 1\n");
			a^=1;
		}
		else{
			if(b) printf("4 1\n");
			else printf("4 3\n");
			b^=1;
		}
	}
}

CF1103B Game with modulo

Description \text{Description} Description

未知一个数 a a a,让你每次猜两个数 x x x y y y,若 ( x   m o d   a ) ≥ ( y   m o d   a ) (x\bmod a)\ge (y\bmod a) (xmoda)(ymoda) 返回 x,否则返回 y。让你猜测次数少于 60 60 60 次的时候猜出数 a a a
a ≤ 2 × 1 0 9 a\le 2\times 10^9 a2×109

Solution \text{Solution} Solution

一开始,令 x = 1 x=1 x=1
不断进行一下过程:

  1. 询问 x x x 2 x 2x 2x
  2. x   m o d   a < 2 x   m o d   a x\bmod a< 2x\bmod a xmoda<2xmoda,说明 a > 2 x a>2x a>2x,令 x ← 2 x x\gets 2x x2x,回到第一步。
  3. x   m o d   a > 2 x   m o d   a x\bmod a> 2x\bmod a xmoda>2xmoda,说明 a ≤ 2 x a\le 2x a2x,也就是 a ∈ ( x , 2 x ] a\in (x,2x] a(x,2x],进入第四步。
  4. 现在已知 a ∈ ( x , 2 x ] a\in (x,2x] a(x,2x],二分找到 a a a 的值即可。

a = 1 a=1 a=1 的情况比较恶心,建议特判处理。

Code \text{Code} Code

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
const int N=4e5+100;
inline ll read(){
  ll x(0),f(1);char c=getchar();
  while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
  while(isdigit(c)) {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
  return x*f;
}
int n,m;
char c;
inline char ask(int x,int y){
  printf("? %d %d\n",x,y);fflush(stdout);
  scanf(" %c",&c);
  return c;
}
char s[105];
signed main(){
#ifndef ONLINE_JUDGE
  //freopen("a.in","r",stdin);
  //freopen("a.out","w",stdout);
#endif    
  while(1){
    scanf(" %s",s+1);
    if(s[1]=='m'||s[1]=='e') return 0;
    int x=1;
    while(1){
      if(ask(x,x<<1)=='x') break;
      else x<<=1;
    }
    if(x==1){
      if(ask(2,1)=='y') printf("! 2\n");
      else printf("! 1\n");
      fflush(stdout);
    }
    else{
      int st=x+1,ed=x<<1;
      while(st<ed){
	int mid=(st+ed)>>1;
	if(ask(mid,x)=='x') st=mid+1;
	else ed=mid;
      }
      printf("! %d\n",st);fflush(stdout);
    }
  }
  return 0;
}
/*

*/

CF1103C Johnny Solving

Description \text{Description} Description

给出一张无重边的无向图(保证每个点度数大于等于 3 3 3)和一个限制 k k k,需要你构造以下两种情况中的一种:

1、找出一条路径长度为 n / k n/k n/k

2、找出 k k k 个环,使得每个环的长度大于 3 3 3 而且不是 3 3 3 的倍数,并且要求保证每个环中至少有一个点在这 k k k 个环里只出现一次。

Solution \text{Solution} Solution

不错的一道题。
不难想到先建出 dfs 生成树的套路。
然后,若直径大于 n / k n/k n/k,就已经满足条件一了。
否则,必然有每个结点的深度不超过 n / k n/k n/k,那么叶子的数量必然不少于 k k k 个。
同时,每个叶子 u u u 必然至少有两条非树返祖边。,假设分别连向 x , y x,y x,y
分类讨论一下一定有一个不是 3 3 3 的倍数的环。
把叶子作为代表结点即可。

Code \text{Code} Code

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
const int N=5e5+100;
inline ll read(){
  ll x(0),f(1);char c=getchar();
  while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
  while(isdigit(c)) {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
  return x*f;
}
int n,m,k;
struct node{
  int to,nxt;
}p[N<<1];
int fi[N],cnt;
inline void addline(int x,int y){
  p[++cnt]=(node){y,fi[x]};fi[x]=cnt;
  return;
}
bool vis[N],jd[N];
vector<int>v[N];
int fa[N],dep[N];
void dfs(int x,int f){
  fa[x]=f;dep[x]=dep[f]+1;vis[x]=1;
  for(int i=fi[x];~i;i=p[i].nxt){
    int to=p[i].to;
    if(to==f) continue;
    if(vis[to]){
      if(dep[to]<dep[x]) v[x].push_back(to);      
    }
    else{
      jd[x]=1;dfs(to,x);
    }
  }
  return;
}
void print(int x,int tp){
  //printf("%d\n",dep[x]-dep[tp]+1);
  while(x!=fa[tp]) printf("%d ",x),x=fa[x];
  putchar('\n');
  return;
}
inline bool ok(int o){return o>2&&o%3;}
bool bac[4];
bool pd[N];
signed main(){
#ifndef ONLINE_JUDGE
  freopen("a.in","r",stdin);
  freopen("a.out","w",stdout);
#endif
  memset(fi,-1,sizeof(fi));cnt=-1;
  n=read();m=read();k=read();
  for(int i=1;i<=m;i++){
    int x=read(),y=read();
    addline(x,y);addline(y,x);
  }
  dfs(1,0);
  for(int i=1;i<=n;i++){
    if(1ll*dep[i]*k>=n){
      printf("PATH\n");
      printf("%d\n",dep[i]);
      print(i,1);return 0;
    }
  }
  printf("CYCLES\n");
  for(int i=1;i<=n&&k;i++){
    int x(0),y(0),z(0);bac[0]=bac[1]=bac[2]=0;
    if(jd[i]) continue;
    for(auto now:v[i]){
      if(pd[now]) continue;
      if(ok(dep[i]-dep[now]+1)){
	printf("%d\n",dep[i]-dep[now]+1);
	pd[i]=1;
	print(i,now);k--;break;
      }
      else if(x&&ok(abs(dep[x]-dep[now])+1+1)){
	if(dep[x]>dep[now]) swap(x,now);
	printf("%d\n%d ",dep[now]-dep[x]+1+1,i);
	print(now,x);
	pd[i]=1;k--;
	break;
      }
      else if(y&&ok(abs(dep[y]-dep[now])+1+1)){
	if(dep[y]>dep[now]) swap(y,now);
	printf("%d\n%d ",dep[now]-dep[y]+1+1,i);
	print(now,y);
	pd[i]=1;k--;
	break;
      }
      else if(z&&ok(abs(dep[z]-dep[now])+1+1)){
	if(dep[z]>dep[now]) swap(z,now);
	printf("%d\n%d ",dep[now]-dep[z]+1+1,i);
	print(now,z);
	pd[i]=1;k--;
	break;
      }
      if(!bac[dep[now]%3]){
	bac[dep[now]%3]=1;
	if(!x) x=now;
	else if(!y) y=now;
	else if(!z) z=now;
      }
    }
  }
  return 0;
}
/*

*/

CF1103D Professional layer

Description \text{Description} Description

给定 1 ≤ n ≤ 1 0 6 1 \le n \le 10^6 1n106 个正整数 a i , 1 ≤ a i ≤ 1 0 12 a_i,1 \le a_i≤10^{12} ai,1ai1012,以及正整数 1 ≤ k ≤ 1 0 12 1 \le k \le 10^{12} 1k1012,修改第 i i i 个正整数 a i a_i ai 的花费为 e i , 1 ≤ e i ≤ 1 0 9 e_i,1 \le e_i \le 10^9 ei,1ei109

要求选出若干个正整数进行一次修改,使得修改后所有正整数的最大公约数等于 1 1 1 。修改操作为:对于正整数 a a a,选择 a a a 的一个约数 d ≤ k d \le k dk,把 a a a 修改为 $ \frac{a}{d}$。

设选出并修改的正整数有 x x x 个,他们的花费之和为 y y y,则总的修改花费为 x × y x \times y x×y。求最小花费。

Solution \text{Solution} Solution

毒瘤状压题。
不难想到,公因数的质因子不超过 11 11 11 个,可以状压。
关键就是对复杂度的优化。
(以下设质因子个数为 k k k
朴素 dp 的复杂度是 O ( n k 3 k ) O(nk3^k) O(nk3k),难以通过。
考虑优化。
首先,注意到会有大量的重复数(打表可知不同的数只有 1 0 4 10^4 104 个),每种数字取代价最小的前 k k k 个即可。
然后,数的数里降到了十万级别,我们可以暴力求出每个数可以转移到那些质因数集合,而每个集合其实也只需要取前 k k k 个数进行转移,所以转移数降到了 k × 2 k k\times2^k k×2k
对每个转移集合的补集枚举子集转移,再加上枚举一维选取个数的复杂度,总复杂度 3 k × k 2 3^k\times k^2 3k×k2
注意每次不能暴力更新滚动数组,复杂度会假掉,所以需要开个 vector 记录一下更新了哪些状态。
注意开 longlong。

Code \text{Code} Code

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
const int N=2e6+100;
inline ll read(){
  ll x(0),f(1);char c=getchar();
  while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
  while(isdigit(c)) {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
  return x*f;
}
int n,m;
ll k,flag;
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
struct node{
  ll a,c;
}p0[N],p[N];
bool cmpc(node x,node y){return x.c<y.c;}
bool cmpa(node x,node y){return x.a<y.a;}
ll tot,g;
ll dp[2][13][2105],now,nxt;
ll prime[13],cnt;
map<ll,int>mp;
vector<int>v[2105];
ll w[13],mi[13],bit[2105];
vector<int>vv[N];
void findtrans(int id){
  ll x=p[id].a;
  //ll x=p[i].a;
  for(int j=1;j<=cnt;j++){
    w[j]=1;
    while(x%prime[j]==0){
      x/=prime[j];w[j]*=prime[j];
    }
  }
  for(int s=0;s<(1<<cnt);s++){
    ll o=1;int ss=s;
    if((int)v[s].size()==cnt) continue;
    while(ss){
      o*=w[bit[ss&-ss]+1];ss-=ss&-ss;
    }
    if(o<=k){
      //v.push_back(s);
      v[s].push_back(id);
      //printf("  s=%d o=%lld\n",s,o);
    }
  }
}
void init(){
  int tp=floor(sqrt(g));
  for(int i=2;i<=tp;i++){
    if(g%i==0){
      prime[++cnt]=i;
      while(g%i==0) g/=i;
    }
  }
  if(g>1) prime[++cnt]=g;
  sort(p0+1,p0+1+n,cmpc);
  for(int i=1;i<=n;i++){
    int x=p0[i].a;
    if(mp.find(x)!=mp.end()&&mp[x]>=cnt) continue;
    p[++tot]=p0[i];mp[x]=mp[x]+1;
    /*for(int j=1;j<=cnt;j++){
      if(x%prime[j]==0){
	p[++tot]=p0[i];mp[x]=mp[x]+1;break;
      }
      }*/
  }
  mi[0]=1;bit[1]=0;
  for(int i=1;i<=cnt;i++) mi[i]=mi[i-1]<<1,bit[mi[i]]=i;
  for(int i=1;i<=tot;i++) findtrans(i);
  //sort(p+1,p+1+tot,cmpa);
  for(int i=0;i<(1<<cnt);i++){
    for(auto x:v[i]) vv[x].push_back(i);
  }
}
vector<int>tr;
void DP(){
  memset(dp,0x3f,sizeof(dp));dp[now=1][0][0]=0;
  for(int i=1;i<=tot;i++){
    //swap(nxt,now);
    tr.clear();
    for(auto ns:vv[i]){
      int t=((1<<cnt)-1)^ns,T=t;
      //debug("ns=%d\n",ns);
      dp[nxt][1][ns]=min(dp[nxt][1][ns],p[i].c);
      tr.push_back(ns);
      for(;t;t=T&(t-1)){
	tr.push_back(t|ns);
	for(int o=0;o<cnt;o++){
	  //debug("ns=%d t=%d o=%d\n",ns,t,o);
	  dp[nxt][o+1][t|ns]=min(dp[nxt][o+1][t|ns],dp[now][o][t]+p[i].c);
	}
      }
    }
    for(auto s:tr){
      for(int o=0;o<=cnt;o++) dp[now][o][s]=min(dp[now][o][s],dp[nxt][o][s]);
    }
    /*
    for(int s=0;s<(1<<cnt);s++){
      for(int o=0;o<=cnt;o++){
	dp[nxt][o][s]=min(dp[nxt][o][s],dp[now][o][s]);
	//if(dp[nxt][o][s]<1e9) printf("s=%d num=%d dp=%lld\n",s,o,dp[nxt][o][s]);
      }	
      }*/
  }
}
void findans(){
  ll ans(2e18),op;
  for(int i=0;i<=cnt;i++){
    if(dp[now][i][(1<<cnt)-1]<1e18){
      if(ans>dp[now][i][(1<<cnt)-1]*i) op=i;
      ans=min(ans,dp[now][i][(1<<cnt)-1]*i);
    }
  }
  printf("%lld\n",ans>1e18?-1:ans);
  //if(flag) printf("g=%lld op=%lld tot=%lld\n",g,op,tot);
  //debug("ans=%lld num=%lld\n",ans,op);
}
signed main(){
#ifndef ONLINE_JUDGE
  freopen("a.in","r",stdin);
  freopen("a.out","w",stdout);
#endif
  n=read();k=read();
  for(int i=1;i<=n;i++) p0[i].a=read(),g=gcd(g,p0[i].a);
  for(int i=1;i<=n;i++) p0[i].c=read();
  //printf("g=%lld\n",g);
  flag=p0[1].a==204367626045ll;
  init();//debug("ok\n");
  DP();
  findans();
  return 0;
}
/*

*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值