531div3链接
CF group链接
完成时间:2019/2/27晚上+28号中午
A.Integer Sequence Dividing
题意:
给一个正整数n,把1,2.3…n这n个数分到两个集合A,B中,sum(A)表示集合A中所有数之和,sum(B)同理,求sum(A)和sum(B)差的绝对值的在最小值
分析:
送分题,只需要求1+2+…n是奇数还是偶数即可,是偶数的话答案就是0,否则答案是1
B.Array K-Coloring
题意:
题意有点儿不是很好读懂。意思是给k(编号1到k)种颜色,n个物品,每个物品有一个数值。
现在要求给每个物品上色,要求数值相同的物品不能涂相同的颜色,且不能有某种颜色没有用到。
如果有满足题目要求的涂色方式,输出YES(当时wa的那一发就是因为没有输出YES),然后按顺序输出每个物品的颜色(任意一种方案即可)。
如果做不到,直接输出NO。
分析:
题目有两个限制条件,要保证所有颜色都要用到必须满足n>=k,要满足数值相同的物品不同色必须满足同一数值的物品数不大于k。
满足这两个条件,就有答案。开一个结构体数组,记录id,按数值排序,循环涂色,然后再按id排序还原,直接输出颜色。
我的AC代码
/*
2019-02-27 15:54:28 apare B - Array K-Coloring GNU C++17 Accepted 31 ms 1200 KB
*/
#include
#define LL long long
#define For(i,a,b) for(int i=(a);i<=(b);++i)
#define Rep(i,a,b) for(int i=(a);i>=(b);--i)
#define Mst(a,b) memset(a,(b),sizeof(a))
using namespace std;
const int maxn = 1e5;
const int mod = 1e9+7;
struct Node{
int id,x,color;
}node[maxn];
int main()
{
int n,k;
scanf("%d%d",&n,&k);
For(i,1,n)
scanf("%d",&node[i].x),node[i].id = i;
if(k>n)
{
cout<<"NO"<<endl;
return 0;
}
sort(node+1,node+1+n,[](Node a,Node b){return a.x<b.x;});
int cnt = 1;
int tot = 1;
int kk = 1;
node[1].color = 1;
node[n+1].x = 5002;
node[n+1].id = 100000;
For(i,2,n+1)
{
if(++kk>k) kk = 1;
node[i].color = kk;
if(node[i].x!=node[i-1].x)
{
tot = max(tot,cnt),cnt = 1;
if(tot>k)
{
cout<<"NO\n";return 0;
}
}
else ++cnt;
}
sort(node+1,node+1+n,[](Node a,Node b){return a.id<b.id;
});
printf("YES\n%d",node[1].color);
For(i,2,n)
printf(" %d",node[i].color);
printf("\n");
return 0;
}
C.Doors Breaking and Repairing
题意:
有n个门,每个门有一个初始的使用年限ai(1<=ai<=1e9),你和另一个人轮流操作。你先手,搞破坏,对某一扇门的损害为x,对方后手,修门,增加某扇门的使用年限y个单位的值。如果你的伤害>=门的年限,那么门的年限变为0,报废且无法修复。
现在有接近无穷多的回(10^100),你尽可能希望使更多门报废,对方尽可能使更少的门报废。两人都很“聪明”。问最后最多几扇门可以被你报废掉?
分析:
如果你的破坏值x比他的修复值y要大,那么架不住回合太多,门一定会都被你报废掉。
如果你的破坏值x<=y,那么你破坏那一扇门,他就跟着修复那一扇门,除非你能一步到位,一次把门年限降为0,否则门的年限只会越来越长。统计ai<=x的门的个数为b,那么你能报废掉一半,如果b是奇数,你能报废(b+1)/2,因为你是先手
我的AC代码
/*
2019-02-27 15:58:39 apare C - Doors Breaking and Repairing GNU C++17 Accepted 31 ms 400 KB
*/
#include
#define LL long long
#define For(i,a,b) for(int i=(a);i<=(b);++i)
#define Rep(i,a,b) for(int i=(a);i>=(b);--i)
#define Mst(a,b) memset(a,(b),sizeof(a))
using namespace std;
const int maxn = 1e5+100;
const int mod = 1e9+7;
int n,x,y;
int a[maxn];
int main()
{
scanf("%d%d%d",&n,&x,&y);
For(i,0,n-1) scanf("%d",a+i);
if(x>y)
{
cout<<n<<endl;
return 0;
}
int cnt = 0;
for(int i=0;i<n;i++)
{
if(a[i]<=x) cnt++;
}
int ans = cnt/2+cnt%2;
cout<<ans<<endl;
return 0;
}
D.Balanced Ternary String
题意:
给定一个长度为3*n(n<=1e5)的字符串,字符串只包含’0’, ‘1’, '2’三种字符。现在你要置换掉某些字符为{‘0’,‘1’,‘2’}中的某个字符,是得新的字符串中三种字符的个数相同。要求置换的次数最少。输出新的字符串。
如果有多个答案,输出 字典序最小的字符串
分析:
显然是贪心。
情况稍微有一点儿多。
先把字符串遍历一遍,统计数三种字符分别出现的次数
设最终三个字符出现的次数都要为x
若三种字符出现的情况本来就一样,直接输出原串
若三种字符有一种出现次数为x,那么肯定另外两种一个多一个少。多的要变成少的。若多的字符字典序较少的字符大,那么从后往前置换,反之从前往后置换。
若有两个字符次数>x,一个
若有两个次数
我的AC代码
/*
2019-02-27 17:19:24 apare D - Balanced Ternary String GNU C++17 Accepted 46 ms 300 KB
*/
#include
#define LL long long
#define For(i,s,b) for(int i=(s);i<=(b);++i)
#define Rep(i,s,b) for(int i=(s);i>=(b);--i)
#define Mst(s,b) memset(s,(b),sizeof(s))
using namespace std;
const int maxn = 3e5+50;
char s[maxn];
int cnt[3];
int r[3] = {0,1,2};
bool cmp(int a,int b)
{
return cnt[a]<cnt[b];
}
int main()
{
int n;
scanf("%d%s",&n,s+1);
int x = n/3;
For(i,1,n)
{
++cnt[s[i]-'0'];
}
if(cnt[0]==cnt[1]&&cnt[1]==cnt[2])
{
printf("%s\n",s+1);
return 0;
}
int dif = 0;
if(cnt[0]==x)
{
if(cnt[1]>cnt[2]) //要把1变成2
{
dif = cnt[1]-x;
Rep(i,n,1)
{
if(s[i]=='1') s[i] = '2',--dif;
if(dif==0)
{
printf("%s\n",s+1);
return 0;
}
}
}
else if(cnt[2]>cnt[1])
{
dif = cnt[2]-x;
For(i,1,n)
{
if(s[i]=='2') s[i] = '1',--dif;
if(dif==0)
{
printf("%s\n",s+1);
return 0;
}
}
}
}
if(cnt[1]==x)
{
if(cnt[0]>cnt[2])
{
dif = cnt[0]-x;
Rep(i,n,1)
{
if(s[i]=='0') s[i] = '2',--dif;
if(dif==0)
{
printf("%s\n",s+1);
return 0;
}
}
}
else if(cnt[0]<cnt[2])
{
dif = cnt[2]-x;
For(i,1,n)
{
if(s[i]=='2') s[i] = '0',--dif;
if(dif==0)
{
printf("%s\n",s+1);
return 0;
}
}
}
}
if(cnt[2]==x)
{
if(cnt[1]>cnt[0]) //1变0
{
dif = cnt[1] - x;
For(i,1,n)
{
if(s[i]=='1') s[i] = '0',--dif;
if(dif==0)
{
printf("%s\n",s+1);
return 0;
}
}
}
else if(cnt[0]>cnt[1])//0变1
{
dif = cnt[0]-x;
Rep(i,n,1)
{
if(s[i]=='0') s[i] = '1',--dif;
if(dif==0)
{
printf("%s\n",s+1);
return 0;
}
}
}
}
sort(r,r+3,cmp);
int small = r[0];
int hadreduce[3] = {0};
///
if(cnt[r[1]]>x&&cnt[r[2]]>x) //一小两大
{
hadreduce[r[1]] = cnt[r[1]] - x;
hadreduce[r[2]] = cnt[r[2]] - x;
dif = x-cnt[small];
if(small==0) //0最少
{
For(i,1,n)
{
if(s[i]=='0') continue;
if(hadreduce[s[i]-'0']>0) --dif,hadreduce[s[i]-'0']--,s[i] = '0';
if(dif==0)
{
printf("%s\n",s+1);
return 0;
}
}
}
else if(small==2) //2最少,要把1和0变2
{
Rep(i,n,1)
{
if(s[i]=='2') continue;
if(hadreduce[s[i]-'0']>0) --dif,hadreduce[s[i]-'0']--,s[i] = '2';
if(dif==0)
{
printf("%s\n",s+1);
return 0;
}
}
}
else //1最少,要把0和2变成1
{
For(i,1,n)
{
if(hadreduce[2]==0) break;
if(s[i]=='1') continue;
if(s[i]=='2'&&hadreduce[2]>0) s[i] = '1',--dif,hadreduce[2]--;
}
Rep(i,n,1)
{
if(hadreduce[0]==0) break;
if(s[i]=='1') continue;
if(s[i]=='0'&&hadreduce[0]>0) s[i] = '1',--dif,hadreduce[0]--;
}
printf("%s\n",s+1);
return 0;
}
}
if(cnt[r[0]]<x&&cnt[r[1]]<x) //一大两小
{
int waitadd[3]={0};
int big = cnt[r[2]]-x;
waitadd[r[1]] = x-cnt[r[1]];
waitadd[r[0]] = x-cnt[r[0]];
if(r[2]==0) //0变为1和2
{
Rep(i,n,1)
{
if(s[i]!='0') continue;
if(waitadd[2]>0) --waitadd[2],--big,s[i] = '2';
else if(waitadd[1]>0) --waitadd[1],--big,s[i] = '1';
else break;
}
printf("%s\n",s+1);
return 0;
}
else if(r[2]==2) //2变为0和1
{
For(i,1,n)
{
if(s[i]!='2') continue;
if(waitadd[0]>0) --waitadd[0],--big,s[i] = '0';
else if(waitadd[1]>0) --waitadd[1],--big,s[i] = '1';
else break;
}
printf("%s\n",s+1);
return 0;
}
else if(r[2]==1) //1变为0和2
{
For(i,1,n)
{
if(waitadd[0]==0) break;
if(s[i]=='1')
{
waitadd[0]--,big--,s[i] = '0';
}
}
Rep(i,n,1)
{
if(waitadd[2]==0) break;
if(s[i]=='1')
{
waitadd[2]--,big--,s[i] = '2';
}
}
printf("%s\n",s+1);
return 0;
}
}
return 0;
}
E.Monotonic Renumeration 题意:
给一组数a[n],n个(2<=n<=2e5),现在要求构造一个长度同样为n的数列b,要求:
1.若a中两个数相同,则b中相同位置的两个数也要相同。
2.b中第一个数必须为0,后面的数只能选择和前一个相同或者比前一个大1,求能构造出的数列的个数(对998244353取模)
分析:
我的AC代码
/*
2019-02-27 17:42:52 apare E - Monotonic Renumeration GNU C++17 Accepted 234 ms 15600 KB
*/
#include
#define LL long long
#define For(i,a,b) for(int i=(a);i<=(b);++i)
#define Rep(i,a,b) for(int i=(a);i>=(b);--i)
#define Mst(a,b) memset(a,(b),sizeof(a))
using namespace std;
const int maxn = 1e6+50;
const int mod = 998244353;
void print(pair<int,int> & x)
{
cout<<"("<<x.first<<","<<x.second<<")\n";
}
void fast_pow(LL a,LL b)
{
LL ans = 1;
while(b)
{
if(b&1) ans = ans*a%mod;
a = a*a%mod;
b>>=1;
}
cout<<ans<<endl;
}
int a[maxn];
map<int,int> mp;
map<int,int> st;
map<int,int> seen;
map<int,int> cnt;
vector<pair<int,int> >v;
int main()
{
int n;
scanf("%d",&n);
For(i,1,n)
{
scanf("%d",a+i);
++cnt[a[i]];
}
For(i,1,n)
{
if(cnt[a[i]]==1) continue;
if(!seen[a[i]]) st[a[i]] = i;
if(++seen[a[i]]==cnt[a[i]]) v.push_back(make_pair(st[a[i]],i));
}
if(v.empty())
{
fast_pow(2ll,n-1);
return 0;
}
sort(v.begin(),v.end());
int cntt = v[0].first-1;
int left = v[0].first;
int right = v[0].second;
for(auto &x:v)
{
// print(x);
if(x.first>right)
{
cntt += x.first-right;
right = x.second;
left = x.first;
}
else
{
right = max(right,x.second);
}
}
cntt += n-right;
fast_pow(2ll,cntt);
return 0;
}
F.Elongated Matrix
题意:
给一个n行m列的矩阵,你可以交换任意两行无限次。交换后对与每一列从上往下遍历,遍历到该列的最后一个元素后以下一列的第一行的元素重新开始,从上往下走。最后生成一个长度为m乘以n的数列,设x为该某个数列任意两个相邻元素之差的绝对值的最小值。求x的最大值。
分析:
先算一下所有的行的排列的数目,是16!
我先写了一发暴力dfs,果然TLE了… 我真的会算复杂度
这道题正确的做法是状压dp。把每一行看作是一个节点。就是要求一条含有n个节点的路径。
可以用一个整数记录一个人状态。16行对应16个节点。一个整数的二进制形式有几个1,说明已经选了几行。二进制的第k为代表第k行。
那么我们可以从1到(1<
如某个状态的二进制形式为010011,那么说明第1行,第2行和第5行已经被选择了。但是我们不知道顺序。
所以我们从这3个1中选出所有可能的当前路径起点和终点(从1里面挑),然后由终点继续向没有走过的顶点(状态中的0的)延伸。
从小到大dp即可。
我的AC代码
//2019-02-28 14:35:51 apare F - Elongated Matrix GNU C++17 Accepted 358 ms 66500 KB
#include
#define For(i,a,b) for(int i=(a);i<=(b);++i)
#define Mst(a,b) memset(a,(b),sizeof(a))
using namespace std;
int dp[1<<16][16][16],a[16][10001],ed,m,n,ans;
int MinDisRow[16][16],MinDisHeadTail[16][16];
void init();void DP();void Solve();void reinit();
int main()
{
std::ios::sync_with_stdio(false);
while(cin>>n>>m)
{
For(i,0,n-1) For(j,0,m-1) cin>>a[i][j];
if(n==1)
{
ans = 2e9;
For(j,1,m-1) ans = min(ans,abs(a[0][j]-a[0][j-1]));
cout<<ans<<endl;;continue;
}
init();DP();Solve();reinit();
}
return 0;
}
void init()
{
ans = 0;
ed = (1<<n)-1;
For(i,0,n-1)
{
For(j,0,n-1)
{
if(i==j) continue;
MinDisRow[i][j] = MinDisHeadTail[i][j] = 2e9;
For(k,0,m-1) MinDisRow[i][j] = min(MinDisRow[i][j],abs(a[i][k]-a[j][k]));
For(k,1,m-1) MinDisHeadTail[i][j] = min(MinDisHeadTail[i][j],abs(a[i][k]-a[j][k-1]));
}
}
}
void DP()
{
For(i,1,ed-1)
{
vector<int> v[2];
int bit = 0;
For(j,0,n-1)
v[(i&(1<<j))>0].push_back(j);
if((int)v[1].size()==1)
{
bit = v[1][0];
For(j,0,n-1)
{
if(j==bit) continue;
dp[i|(1<<j)][bit][j] = max(dp[i|(1<<j)][bit][j],MinDisRow[bit][j]);
}
}
else
{
int cnt0 = v[0].size()-1;
int cnt1 = v[1].size()-1;
int j,k,p;
For(jj,0,cnt1)
{
j = v[1][jj];
For(kk,0,cnt1)
{
k = v[1][kk];
if(j==k) continue;
For(pp,0,cnt0) //j->...->K + p ==> j->...->K->p
{
p = v[0][pp];
dp[i|(1<<p)][j][p] = max(dp[i|(1<<p)][j][p],min(dp[i][j][k],MinDisRow[k][p]));
}
}
}
}
}
}
void Solve()
{
For(j,0,n-1)
For(k,0,n-1)
{
if(j==k) continue;
ans = max(ans,min(dp[ed][j][k],MinDisHeadTail[j][k]));
}
cout<<ans<<endl;
}
void reinit()
{
Mst(dp,0);
Mst(MinDisRow,0);
Mst(MinDisHeadTail,0);
}
当时看了别人的题解,还写了个有vis数组的,但是写解题报告的时候觉得完全不用写vis数组,但是时间稍微满了一点点。358ms
中午交的用了vis数组的代码 (218ms)
还有暴力的代码 (TLE)