Codeforces Round #585 (Div. 2)
作者:互联网
# A:水题
B:
当时没有想到题解给出来的方法,写了一个简单dp交上去,炸了一次int。
题意:给出一个序列,求有多少个区间,乘积为正数和负数。
令dp[i][0/1]表示,以i结尾的区间,有多少个正(0)或者负(1)的区间。
如果a~i~>0,那么上一个i-1的正数区间乘以a~i~还是正数,所以dp[i][0]+=dp[i-1[0]+1,负数区间不变。同理负数。最后累加和即可
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<map>
//#include<regex>
#include<cstdio>
#define up(i,a,b) for(int i=a;i<b;i++)
#define dw(i,a,b) for(int i=a;i>b;i--)
#define upd(i,a,b) for(int i=a;i<=b;i++)
#define dwd(i,a,b) for(int i=a;i>=b;i--)
//#define local
typedef long long ll;
const double esp = 1e-6;
const double pi = acos(-1.0);
const int INF = 0x3f3f3f3f;
const int inf = 1e9;
using namespace std;
int read()
{
char ch = getchar(); int x = 0, f = 1;
while (ch<'0' || ch>'9') { if (ch == '-')f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
typedef pair<int, int> pir;
#define lson l,mid,root<<1
#define rson mid+1,r,root<<1|1
#define lrt root<<1
#define rrt root<<1|1
const int N = 2e5 + 10;
int a[N];
int n;
int dp[N][3];
int main()
{
n = read();
upd(i, 1, n)a[i] = read();
upd(i, 1, n)
{
if (a[i] > 0)dp[i][0] = dp[i - 1][0] + 1, dp[i][1] = dp[i - 1][1];
else
{
dp[i][0] = dp[i - 1][1];
dp[i][1] = 1 + dp[i - 1][0];
}
}
ll np = 0, p = 0;
upd(i, 1, n)
{
np +=(ll)dp[i][1];
p += (ll)dp[i][0];
}
cout << np << " " << p << endl;
return 0;
}
c
题意:给出两个字符串只含有a,b,给出一次操作的定义,交换两个字符串的任意位置的字符一次。求最少操作使得两个字符串相同。
我们可以看到字符串只包含有a,b两种形式的字符,其实就是01串问操作次数。
a a a b
b b b a 这两种情况就涵盖掉了所有的不同形式(这里不同要是偶数才行,因为如果是奇数的话那么这个字符串没有办法变得一样总会多出来一个)
而要使得操作次数最小,那么我们就尽量匹配 a a b b 这种类型的。
所以开两个栈记录,不同点是a的,不同点是b的,先把这两个栈自己配对,然后会单下来一边一个,再配对,这样操作次数一定是最小的。
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<map>
//#include<regex>
#include<cstdio>
#define up(i,a,b) for(int i=a;i<b;i++)
#define dw(i,a,b) for(int i=a;i>b;i--)
#define upd(i,a,b) for(int i=a;i<=b;i++)
#define dwd(i,a,b) for(int i=a;i>=b;i--)
//#define local
typedef long long ll;
const double esp = 1e-6;
const double pi = acos(-1.0);
const int INF = 0x3f3f3f3f;
const int inf = 1e9;
using namespace std;
int read()
{
char ch = getchar(); int x = 0, f = 1;
while (ch<'0' || ch>'9') { if (ch == '-')f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
typedef pair<int, int> pir;
#define lson l,mid,root<<1
#define rson mid+1,r,root<<1|1
#define lrt root<<1
#define rrt root<<1|1
const int N = 2e5 + 10;
char s[N];
char t[N];
stack<int >sta,stb;
vector<pir >vec;
int n;
int main()
{
n = read();
cin >> s+1 >> t+1;
upd(i, 1, n)
{
if (s[i] != t[i])
{
if (s[i] == 'a')sta.push(i);
else stb.push(i);
}
}
if ((sta.size() + stb.size()) % 2)cout << "-1" << endl;
else
{
while (sta.size() != 0 && sta.size() != 1)
{
int fi = sta.top(); sta.pop();
int se = sta.top(); sta.pop();
vec.push_back(make_pair(fi, se));
}
while (stb.size() != 0 && stb.size() != 1)
{
int fi = stb.top(); stb.pop();
int se = stb.top(); stb.pop();
vec.push_back(make_pair(fi, se));
}
if (sta.size())
{
int fi = sta.top(); int se = stb.top();
vec.push_back(make_pair(se, se));
vec.push_back(make_pair(fi, se));
}
cout << vec.size() << endl;
for (auto k : vec)
{
cout << k.first << " " << k.second << endl;
}
}
return 0;
}
D:博弈论
给出一个序列包含0~9和?,现在两个人进行博弈,每一次变一个问号(个数为偶数个),变成0~9中的数字,问后手的那个人是否能赢得比赛当且仅当这一个序列被变完后1~mid,mid+1~n两边的和相等。
我们设想一下博弈,假设左边的和小于右边。我们先手的人一定会拿右边一个9,为什么呢,因为右边已经大了,为了不平衡一定会继续加大这种不平衡,那么后手的人一定只能取左边一个9,来抵消这种平衡。
所以可发现,一开始的操作没有任何意义,左边和右边的差值没有变化(因为一边拿一个9),直到某一边?被拿完,这时候才开始真正的博弈。
假设右边问号多,那么肯定右边赢全拿零都比左边大了。
左边多,那么开始博弈。我们假定左边比右边小了x,那么就相当于(注意到问好是偶数个,后手必定会拿最后一次)两个人,每一次取0~9,现在问后手能否刚好取到x。那么这个题就变成了这样的经典的问题。所以当x=9/2*dif的时候,后手能赢(dif指的是左边还剩下的问号),理解为先手任意取0~9的数字,我都能后手凑一个数字,使得和为9,那么因为我后手必定最后一次,所以我一定能取到x否则必败。先手不取x%9那个数字,后手一定不能赢。
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<map>
//#include<regex>
#include<cstdio>
#define up(i,a,b) for(int i=a;i<b;i++)
#define dw(i,a,b) for(int i=a;i>b;i--)
#define upd(i,a,b) for(int i=a;i<=b;i++)
#define dwd(i,a,b) for(int i=a;i>=b;i--)
//#define local
typedef long long ll;
const double esp = 1e-6;
const double pi = acos(-1.0);
const int INF = 0x3f3f3f3f;
const int inf = 1e9;
using namespace std;
int read()
{
char ch = getchar(); int x = 0, f = 1;
while (ch<'0' || ch>'9') { if (ch == '-')f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
typedef pair<int, int> pir;
#define lson l,mid,root<<1
#define rson mid+1,r,root<<1|1
#define lrt root<<1
#define rrt root<<1|1
int n;
const int N = 2e5 + 10;
char s[N];
int main()
{
n = read();
cin >> s;
int mid = n / 2;
int lf = 0, rt = 0;
int ql = 0,qr = 0;
up(i, 0, mid)
{
if (s[i] != '?')
{
lf += s[i] - '0';
}
else ql++;
}
up(i, mid, n)
{
if (s[i] != '?')
{
if (rt += s[i] - '0');
}
else qr++;
}
if (lf < rt&&ql < qr)
{
cout << "Monocarp" << endl; return 0;
}
if (lf > rt&&ql > qr)
{
cout << "Monocarp" << endl; return 0;
}
if (lf == rt&&ql==qr)
{
cout << "Bicarp" << endl; return 0;
}
if (lf < rt)
{
int dis = rt - lf;
if (dis != (ql - qr) / 2 * 9)
{
cout << "Monocarp" << endl;
}
else
{
cout << "Bicarp" << endl;
}
}
else
{
int dis = lf - rt;
if (dis != (qr - ql) / 2 * 9)
{
cout << "Monocarp" << endl;
}
else
{
cout << "Bicarp" << endl;
}
}
return 0;
}
E 状压
做B题时间太久了,写完D后E并没有调出来,实际上写过类似的题目不应该没有ac的。
题意:给出20种颜色,现在要求每一个位置能和相邻的互换颜色,问最小的代价使得每一种颜色只存在于一个连续的区间。
令pre[i][j]为,第j种颜色在第i种颜色后面的时候,需要的代价。(只时候剔除掉所有其他颜色,只考虑这两个颜色),预处理o(n*20*20)既可。
为什么要这样子做呢。
假设一个序列为 1 3 2 1 2 3 1
我们计算一下变成 1 1 1 2 2 3 3 需要的代价是多少。
pre[1][2]=3,换完成后变成 1 1 1 2 2,不考虑3
pre[1][3]=3,完成后变为 1 1 1 3 3,不考虑2
pre[2][3]=2,完成后变成2 2 3 3
那我们直接看看这个序列需要多少代价:2+4+2=8=pre[1][2]+pre[1][3]+pre[2][3]
这不是巧合,因为每一次交换相邻两个会有相对位置不变的结论。pre[1][2]表示不考虑3的时候,1 2的相对位置为1 1 1 2 2 ,那么3可能插入在了某个中间,可能在1里面有3,2里面也有3,所以我们计算pre[1][3],那么1完全再3的前面了就。这时候只剩下2,3的位置并不完全正确,可能2前面还有3,再换2 3,即pre[2][3],交换完成。
故预处理后,我们令status=(1<<20)-1(不懂状态的可以先去看看状压)我们枚举状态,0<=s<=staus,s表示已经排号的颜色种类。那么我们现在假如一个颜色。如上可以看到,代价为\[\sum pre[i][j]|s\&i==1\]
即j可能再s状态里面的每一种颜色里面或者前面,累加和即为代价。
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<map>
//#include<regex>
#include<cstdio>
#define up(i,a,b) for(int i=a;i<b;i++)
#define dw(i,a,b) for(int i=a;i>b;i--)
#define upd(i,a,b) for(int i=a;i<=b;i++)
#define dwd(i,a,b) for(int i=a;i>=b;i--)
//#define local
typedef long long ll;
const double esp = 1e-6;
const double pi = acos(-1.0);
const int INF = 0x3f3f3f3f;
const int inf = 1e9;
using namespace std;
int read()
{
char ch = getchar(); int x = 0, f = 1;
while (ch<'0' || ch>'9') { if (ch == '-')f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
typedef pair<int, int> pir;
#define lson l,mid,root<<1
#define rson mid+1,r,root<<1|1
#define lrt root<<1
#define rrt root<<1|1
const int N = 4e5 + 10;
ll f[1<<21];
ll pre[22][22];
ll a[N];
int n;
int main()
{
n = read();
up(i, 0, n)
{
a[i] = read();
}
up(j, 0, 20)
{
up(k, 0, 20)
{
if (j == k)continue;
ll prej = 0, prek = 0;
up(i, 0, n)
{
if (a[i] == k + 1)prek++;
if (a[i] == j + 1)prej += prek;
}
pre[j][k] = prej;
}
}
// up(j, 0, 20) { up(k, 0, 20) { cout << "pre" << pre[j][k]; }cout << endl; }
int maxn = (1 << 20);
up(i, 0, maxn)f[i] = 1e12;
f[0] = 0;
up(i, 0, maxn)
{
up(j, 0, 20)
{
if (!((i>>j) & 1))
{
ll temp = 0;
up(k, 0, 20)
{
if(1&(i>>k))
temp += pre[k][j];
}
f[i | 1 << j] = min(f[i | 1 << j], f[i] + temp);
}
}
}
cout << f[(1 << 20) - 1]<<endl;
return 0;
}
标签:585,const,int,Codeforces,ch,Div,include,dp,define 来源: https://www.cnblogs.com/LORDXX/p/11607869.html