Southern and Volga Russia Qualifier 2019-2020 gym102348
190754 ワード
文書ディレクトリ A-Yellow Cards(思考) B-Interesting Vertices(dfs遡及) C-Marbles(状圧dp) D-Ticket Game(思考ゲーム) E-Painting The Fence(欲張り+優先キュー) F-The Number of Products(暴力) G-Swap Letters(思考) H-Berland Prospect(リニアdp) I-Radio Stations(2-sat+テクニック建図) J-Monocarp and T-Shirts(期待+反発) K-Moonbound(アナログ) L-Printer(暴力) A-Yellow Cards(思考)
あなたに2つのチームをあげて、人数はそれぞれa 1、a 2 a 1、a 2 a 1、a 2 a 2、a 2で、すべてのチームはすべての人が最大でk 1、k 2 k 1、k 2 k 1、k 2枚のイエローカードを受け取った后に退場させられて、全部でn n n n枚のイエローカードがあって、最低と最大で何人が退場されますかを求めます.
問題の構想を解く:貪欲に求めて、すべての人は同等に考えて、最も少ないのはすべての人が少なくともk−1 k−1枚のカードを受け取ってから場を離れて、最も多いのは優先的にk k kの値の小さい選手です.
AC_code:
B-Interesting Vertices(dfs遡及)
あなたに1課n n n個の節点の木があって、その中にk k個の節点が染色されて、どれだけの節点が自分が染色されていないことを満たして、そのすべての本の木の中で少なくとも1つの節点が染色されていることを求めます.
解題構想:d f s dfs dfs遡及は木の重心を求めるように解く.d f s dfs dfs遡及は各ノードのそのサブツリーに染色点があるかどうかを得ることができ、すべてのサブツリーにおける染色点の個数s u m sumを統計し、父に向かうサブツリーに染色された点数はk−s u m k−sum−sumである.これにより,このノードが条件を満たすか否かを判断できる.
AC_code:
C-Marbles(状圧dp)
あなたに1つの配列をあげて、毎回あなたは隣接する2つの要素を选んで交换することができて、あなたに最も少ない交换の回数を求めて、これは配列のすべての同じ要素とすべて1つになります.
解題構想:配列の要素は最大20 20 20個しかないので、20 20ビットの状圧d p dpでこの問題を解決することができ、d p[i]dp[i]dp[i]dp[i]はi i iの状態が表す数字がすでに1つになっており、前の最低交換回数にランクされていることを示している.転移は、d p[i]=m i n(d p[i],d p[i(1<AC_code:
D-Ticket Game(思考ゲーム)
文字列をあげます.文字「0」~「9」と「?」「?」「?」を含みます.誰もが「?」?前後して0~9を記入し、Monocarpが先手で、最後の文字列の前半の数字と後半に等しい場合はBicarpが勝つ.
問題を解く構想:1、まず「に対して」?前半のみ、または右半分のみの場合、Monocarpがどのように記入してもBicarpは2人に1つの疑問符を記入させた後、疑問符の半分を9 9 9 9 9増加させることができることが分かったので、両側に92、どちらにも疑問符がついているとき、このときの二人の強みは同じなので、元の状態を維持するしかありません(ある半分をどれだけ増やしたいのか、私はもう半分を増やして、あなたが望む状態を得られないようにします).だから、最後には必ずまた1の状態に戻ります.
AC_code:
E-Painting The Fence(欲張り+優先キュー)
あなたにm mのm种类の数字の全部でn n n个をあげて、あなたを前から后ろに埋めさせて、同じ数字は最大でk k个を连続して、あなたに构造の方案を出力させて、构造を构造することができません
问题を解く考え方:贪欲な考え方、优先的に数量の多い先を选んで、このように最后に同じ数字の数量が最も少なくなることができて、だから私达は优先的に数量の最も多い2种类の数字を选んで埋めて、最后に残った(ある种)はその前の位置に埋めて、きっと同じと一绪に埋めて、ここは证明しないで、自分で描くことができます.優先キューシミュレーションでいいです.
AC_code:
F-The Number of Products(暴力)
あなたに1つの配列をあげて、あなたに区間の積が負、ゼロ、正の個数になることを求めさせます.
直接暴力で探せばいいので、おつりを探すときは気をつけてください.
AC_code:
G-Swap Letters(思考)
あなたに2つの文字列をあげて、ただ‘a’’’’b’’‘a’’b’’’’’’’’‘a’’b’の2種類の文字だけあって、毎回操作してあなたはそれぞれ2つの文字列の中の2つの文字を選んで交換することができて、少なくとも何回の操作を聞いて2つの文字列を同じにすることができて、‘−1’‘−1’’’‘−1’’’’’’’’’’‘−1’’’’’’’’’’’’’’’
問題を解く構想:貪欲に解いて、位置が同じで文字が同じで交換しないで、それでは最後にa b ab abあるいはb a ba baのこのような文字の対しか残っていません.2対の同じa b ab abあるいはb a baは1回の交換を通じてこの2対を同じにすることができて、1対のa b abとb a baは2回交換する必要があります.直接答えを統計すればいいです.
AC_code:
H-Berland Prospect(リニアdp)
あなたに1つの増加する配列をあげて、あなたに1つの最も長い等差のシーケンスを探して、出力の長さ.
解題構想:d p[i][j]dp[i][j]dp[i][j]dp[i][j]は位置i iから公差k=a[j]−a[i]k=a[j]−a[i]k=a[j]−a[i]k=a[j]−a[i]の最長シーケンスを表し、では、後ろから、d p[i][j]=m a x(d p[i][j],m a x(2,d p[j][i d d x[a[j]+k]+1))dp[i][j]=max(dp[i][j],max(2,dp[j][idx[a[j]+k]+1))dp[i][j]=max(dp[i]+j],max(2,dp[j]+idx[a[j]+k]+1))があり、最大値を記録すればよい.注意:この問題カードm a p Q A Q mapQQQQQQQQはm a p map mapでT T Tを離散化し、二分して下書きを探す必要がある.
AC_code:
I-Radio Stations(2-sat+テクニック建図)
n n n nの都市はラジオ局を配置しなければならなくて、第i iの都市は少なくともx i xiあるいはy i yi yi yi yiの中の1つをインストールしなければならなくて、次にあなたにp p p pのpのラジオ局をあげて、ラジオ局i i i i iの電力範囲はl i-r i li-ri li-ri riで、最後にmの制限条件があって、第i iの制限条件はラジオ局u i ui uiとv i viはすべてインストールすることができません最後に、n+m n+m n+mの条件を満たし、少なくとも1つの電力値f f f fが選択したk k局がl i<=f<=r i li<=f<=ri li<=f<=riを満たし、k k k kとf f f f f fを出力し、どのk局を選択したかを満たすように、局の設置案を見つけます.
解題構想:電力範囲の制限がなければ、この問題は裸の2−s a t 2−sat 2−sat問題であり、現在電力範囲の制限があり、これらの制限条件も2−s a t 2−sat 2−sat 2−satのモデルに変換する必要がある.ここのn+m n+mの条件の連辺方式は私は言わない.主に電力範囲の連辺方式について話している.W∗2 W*2 W∗2点を再構築し、点i i iは条件f>=i f>=i f>=iを選択したことを示し、逆に点i+Wi+Wi+WはfAC_code:
J-Monocarp and T-Shirts(期待+反発)
よく知られているように、ACMを打つと服を出すことができます.あなたはn試合があります.試合ごとにPの確率でx-1の大きさの服を手に入れます.Qの確率でx+1の大きさの服を手に入れます.だから、1-P-Qの確率でxの大きさの服を手に入れます.同時に、n人の友达がn枚の大きさの異なる服を手に入れたいと思っています.友达の数の期待を満たすことができます.
解題構想:友人の個数の期待を満たす=友人ごとに満たされる確率の和であるため,友人ごとに満たされる確率を求めればよい.確率を計算するときは反発すればよい(0.8 0.8 0.8の確率でx xを満たすと同時に0.7 0.7の確率でx x xを満たす.2つの確率は異なるイベントであるため、p(x)=0.8+0.7−0.8∗0.7 p(x)=0.8+0.7*0.7 p(x)=0.8+0.7−0.8∗0.7 p(x)=0.8+0.7−0.8*0.7 p(x)=0.8+0.7−0.8∗0.7)
AC_code:
K-Moonbound(アナログ)
最后にi行目j列を(i+j)%2==0 stand:stoneにして、11の充填を1つ选ぶことができて、2*2の充填を选ぶことができて、ある位置はそれを満たすことができて、后者は隣接してすでに充填されています.
問題を解く構想:シミュレーションすればよい
AC_code:
L-Printer(暴力)
2阶建てのビルがあって、上に下りてk k k时间を必要として、毎阶の隣の位置の移动は1 1 1时间を使って、ある位置からすべての1 1 1 1位置の时间の最大値の最小値と最大値を求めます.
問題解決の考え方:暴力
AC_code:
あなたに2つのチームをあげて、人数はそれぞれa 1、a 2 a 1、a 2 a 1、a 2 a 2、a 2で、すべてのチームはすべての人が最大でk 1、k 2 k 1、k 2 k 1、k 2枚のイエローカードを受け取った后に退場させられて、全部でn n n n枚のイエローカードがあって、最低と最大で何人が退場されますかを求めます.
問題の構想を解く:貪欲に求めて、すべての人は同等に考えて、最も少ないのはすべての人が少なくともk−1 k−1枚のカードを受け取ってから場を離れて、最も多いのは優先的にk k kの値の小さい選手です.
AC_code:
#include
#pragma GCC optimize(2)
#define bug printf("*********
");
#define mem0(a) memset(a, 0, sizeof(a));
#define mem1(a) memset(a, -1, sizeof(a));
#define finf(a, n) fill(a, a+n, INF);
#define lowbit(x) x&-x
#define fuck(x) cout<
#define ios ios::sync_with_stdio(false);
#define pb(x) push_back(x)
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<LL, pair<int, LL> > LLppar;
typedef pair<int, int> par;
typedef pair<LL, int> LLpar;
const LL mod = 998244353;
const int inf = 1e9+7;
const LL INF = 1e18+7;
const int base = 131; //19260817 233 1331 1e9+7
const double eps = 0.000001;
int a1, a2, k1, k2, n;
int main() {
#ifndef ONLINE_JUDGE
//freopen("out.txt", "w", stdout);
//freopen("in.txt", "r", stdin);
#endif
while(~scanf("%d%d%d%d%d", &a1, &a2, &k1, &k2, &n)) {
int ans1 = max(0, n-a1*(k1-1)-a2*(k2-1)), ans2 = 0;
if(k1 < k2) ans2 += min(a1, n/k1), n -= ans2*k1, ans2 += min(a2, n/k2);
else ans2 += min(a2, n/k2), n -= ans2*k2, ans2 += min(a1, n/k1);
printf("%d %d
", ans1, ans2);
}
#ifndef ONLINE_JUDGE
//cout <
#endif
return 0;
}
B-Interesting Vertices(dfs遡及)
あなたに1課n n n個の節点の木があって、その中にk k個の節点が染色されて、どれだけの節点が自分が染色されていないことを満たして、そのすべての本の木の中で少なくとも1つの節点が染色されていることを求めます.
解題構想:d f s dfs dfs遡及は木の重心を求めるように解く.d f s dfs dfs遡及は各ノードのそのサブツリーに染色点があるかどうかを得ることができ、すべてのサブツリーにおける染色点の個数s u m sumを統計し、父に向かうサブツリーに染色された点数はk−s u m k−sum−sumである.これにより,このノードが条件を満たすか否かを判断できる.
AC_code:
#include
#pragma GCC optimize(2)
#define bug printf("*********
");
#define mem0(a) memset(a, 0, sizeof(a));
#define mem1(a) memset(a, -1, sizeof(a));
#define finf(a, n) fill(a, a+n, INF);
#define lowbit(x) x&-x
#define fuck(x) cout<
#define ios ios::sync_with_stdio(false);
#define pb(x) push_back(x)
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<LL, pair<int, LL> > LLppar;
typedef pair<int, int> par;
typedef pair<LL, int> LLpar;
const LL mod = 1e9 + 7;
const int inf = 1e9 + 7;
const LL INF = 1e18 + 7;
const int base = 131; //19260817 233 1331 1e9+7
const double eps = 0.000001;
int n, k, cnt;
int c[200010], head[200010], vis[200010], sz[200010];
struct edge {
int to, nxt;
}e[200010*2];
void add(int u, int v) {
e[cnt].to = v;
e[cnt].nxt = head[u];
head[u] = cnt ++;
}
vector<int> ans;
void dfs(int u) {
vis[u] = 1;
sz[u] = c[u];
int flag = 1;
for(int i = head[u]; ~i; i = e[i].nxt) {
int en = e[i].to;
if(vis[en]) continue;
dfs(en);
if(!sz[en]) flag = 0;
sz[u] += sz[en];
}
if(!c[u] && flag && (k > sz[u] || u == 1)) ans.push_back(u);
}
int main() {
int x, y;
while(~scanf("%d%d", &n, &k)) {
mem0(vis);mem0(c);mem1(head);
cnt = 0;
ans.clear();
for(int i = 0; i < k; i ++) {
scanf("%d", &x);
c[x] = 1;
}
for(int i = 0; i < n-1; i ++) {
scanf("%d%d", &x, &y);
add(x, y), add(y, x);
}
dfs(1);
sort(ans.begin(), ans.end());
printf("%d
", ans.size());
for(auto i : ans) {
printf("%d ", i);
}
printf("
");
}
return 0;
}
C-Marbles(状圧dp)
あなたに1つの配列をあげて、毎回あなたは隣接する2つの要素を选んで交换することができて、あなたに最も少ない交换の回数を求めて、これは配列のすべての同じ要素とすべて1つになります.
解題構想:配列の要素は最大20 20 20個しかないので、20 20ビットの状圧d p dpでこの問題を解決することができ、d p[i]dp[i]dp[i]dp[i]はi i iの状態が表す数字がすでに1つになっており、前の最低交換回数にランクされていることを示している.転移は、d p[i]=m i n(d p[i],d p[i(1<
#include
#pragma GCC optimize(2)
#define bug printf("*********
");
#define mem0(a) memset(a, 0, sizeof(a));
#define mem1(a) memset(a, -1, sizeof(a));
#define finf(a, n) fill(a, a+n, INF);
#define lowbit(x) x&-x
#define fuck(x) cout<
#define ios ios::sync_with_stdio(false);
#define pb(x) push_back(x)
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<LL, pair<int, LL> > LLppar;
typedef pair<int, int> par;
typedef pair<LL, int> LLpar;
const LL mod = 1e9 + 7;
const int inf = 1e9 + 7;
const LL INF = 1e18 + 7;
const int base = 131; //19260817 233 1331 1e9+7
const double eps = 0.000001;
int n;
int a[400010];
LL dp[1<<20], cost[25][25], pre[25][400010], s[25];
int id[25];
int main() {
while(~scanf("%d", &n)) {
mem1(id);
mem0(cost);
mem0(pre);
int cnt = 0;
for(int i = 1; i <= n; i ++) {
scanf("%d", &a[i]);
if(id[a[i]] == -1) id[a[i]] = cnt ++;
}
for(int i = 0; i < cnt; i ++) {
int now = 1;
s[i] = 0;
for(int j = 1; j <= n; j ++) {
if(id[a[j]] == i) s[i] += abs(j-now ++);
}
//printf("%d %lld
", i, s[i]);
}
for(int i = 0; i < cnt; i ++) {
pre[i][0] = 0;
for(int j = 1; j <= n; j ++) {
pre[i][j] = pre[i][j-1] + (id[a[j]] == i);
}
}
for(int i = 1; i <= n; i ++) {
for(int j = 0; j < cnt; j ++) {
if(id[a[i]] == j) continue;
cost[id[a[i]]][j] += pre[j][n] - pre[j][i];
}
}
for(int i = 0; i < cnt; i ++) {
for(int j = 0; j < cnt; j ++) {
//printf("cost[%d][%d] = %lld
", i, j, cost[i][j]);
}
}
dp[0] = 0;
for(int i = 1; i < 1<<cnt; i ++) {
dp[i] = INF;
int sz = 0;
for(int j = 0; j < cnt; j ++) {
if(!(i&(1<<j))) continue;
LL val = 0;
for(int k = 0; k < cnt; k ++) {
if(!(i&(1<<k)) || k == j) continue;
val += cost[k][j];
}
dp[i] = min(dp[i], dp[i^(1<<j)] + s[j] - val);
}
}
printf("%lld
", dp[(1<<cnt)-1]);
}
return 0;
}
D-Ticket Game(思考ゲーム)
文字列をあげます.文字「0」~「9」と「?」「?」「?」を含みます.誰もが「?」?前後して0~9を記入し、Monocarpが先手で、最後の文字列の前半の数字と後半に等しい場合はBicarpが勝つ.
問題を解く構想:1、まず「に対して」?前半のみ、または右半分のみの場合、Monocarpがどのように記入してもBicarpは2人に1つの疑問符を記入させた後、疑問符の半分を9 9 9 9 9増加させることができることが分かったので、両側に92、どちらにも疑問符がついているとき、このときの二人の強みは同じなので、元の状態を維持するしかありません(ある半分をどれだけ増やしたいのか、私はもう半分を増やして、あなたが望む状態を得られないようにします).だから、最後には必ずまた1の状態に戻ります.
AC_code:
#include
#pragma GCC optimize(2)
#define bug printf("*********
");
#define mem0(a) memset(a, 0, sizeof(a));
#define mem1(a) memset(a, -1, sizeof(a));
#define finf(a, n) fill(a, a+n, INF);
#define lowbit(x) x&-x
#define fuck(x) cout<
#define ios ios::sync_with_stdio(false);
#define pb(x) push_back(x)
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<LL, pair<int, LL> > LLppar;
typedef pair<int, int> par;
typedef pair<LL, int> LLpar;
const LL mod = 1e9 + 7;
const int inf = 1e9 + 7;
const LL INF = 1e18 + 7;
const int base = 131; //19260817 233 1331 1e9+7
const double eps = 0.000001;
int n;
char s[200010];
int main() {
while(~scanf("%d", &n)) {
scanf("%s", s);
int s1 = 0, s2 = 0, a = 0, b = 0;
for(int i = 0; i < n/2; i ++) {
if(s[i] == '?') s1 ++;
else a += s[i]-'0';
}
for(int i = n/2; i < n; i ++) {
if(s[i] == '?') s2 ++;
else b += s[i]-'0';
}
if(a-b == (s2-s1)/2*9) printf("Bicarp
");
else printf("Monocarp
");
}
return 0;
}
E-Painting The Fence(欲張り+優先キュー)
あなたにm mのm种类の数字の全部でn n n个をあげて、あなたを前から后ろに埋めさせて、同じ数字は最大でk k个を连続して、あなたに构造の方案を出力させて、构造を构造することができません
问题を解く考え方:贪欲な考え方、优先的に数量の多い先を选んで、このように最后に同じ数字の数量が最も少なくなることができて、だから私达は优先的に数量の最も多い2种类の数字を选んで埋めて、最后に残った(ある种)はその前の位置に埋めて、きっと同じと一绪に埋めて、ここは证明しないで、自分で描くことができます.優先キューシミュレーションでいいです.
AC_code:
#include
#pragma GCC optimize(2)
#define bug printf("*********
");
#define mem0(a) memset(a, 0, sizeof(a));
#define mem1(a) memset(a, -1, sizeof(a));
#define finf(a, n) fill(a, a+n, INF);
#define lowbit(x) x&-x
#define fuck(x) cout<
#define ios ios::sync_with_stdio(false);
#define pb(x) push_back(x)
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<LL, pair<int, LL> > LLppar;
typedef pair<int, int> par;
typedef pair<LL, int> LLpar;
const LL mod = 1e9 + 7;
const int inf = 1e9 + 7;
const LL INF = 1e18 + 7;
const int base = 131; //19260817 233 1331 1e9+7
const double eps = 0.000001;
int n, m, k;
int a[200010], s[200010];
struct node {
int x, s;
bool operator<(const node &a) const {
return s < a.s;
}
};
priority_queue<node> q;
int main() {
int x;
while(~scanf("%d%d%d", &n, &m, &k)) {
mem0(s);
mem0(a);
while(!q.empty()) q.pop();
for(int i = 1; i <= m; i ++) {
node cur;
scanf("%d", &cur.s);
cur.x = i;
q.push(cur);
}
int cnt = 0;
while(!q.empty()) {
if(q.size() >= 2) {
node n1 = q.top();q.pop();
node n2 = q.top();q.pop();
a[cnt ++] = n1.x;
a[cnt ++] = n2.x;
n1.s --;
n2.s --;
if(n1.s) q.push(n1);
if(n2.s) q.push(n2);
}else {
if(a[cnt-1] != q.top().x) {
a[cnt ++] = q.top().x;
node cur = q.top();q.pop();
cur.s --;
if(cur.s) q.push(cur);
}
break;
}
}
for(int i = 0; i < cnt && !q.empty(); i ++) {
if(a[i] == q.top().x) {
s[i] += min(q.top().s, k-1);
node cur = q.top();q.pop();
cur.s -= min(cur.s, k-1);
if(cur.s > 0) q.push(cur);
else break;
}
}
if(!q.empty()) printf("-1");
else
for(int i = 0; i < cnt; i ++) {
for(int j = 0; j <= s[i]; j ++) {
printf("%d ", a[i]);
}
}
printf("
");
}
return 0;
}
F-The Number of Products(暴力)
あなたに1つの配列をあげて、あなたに区間の積が負、ゼロ、正の個数になることを求めさせます.
直接暴力で探せばいいので、おつりを探すときは気をつけてください.
AC_code:
#include
#pragma GCC optimize(2)
#define bug printf("*********
");
#define mem0(a) memset(a, 0, sizeof(a));
#define mem1(a) memset(a, -1, sizeof(a));
#define finf(a, n) fill(a, a+n, INF);
#define lowbit(x) x&-x
#define fuck(x) cout<
#define ios ios::sync_with_stdio(false);
#define pb(x) push_back(x)
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<LL, pair<int, LL> > LLppar;
typedef pair<int, int> par;
typedef pair<LL, int> LLpar;
const LL mod = 1e9 + 7;
const int inf = 1e9 + 7;
const LL INF = 1e18 + 7;
const int base = 131; //19260817 233 1331 1e9+7
const double eps = 0.000001;
int n;
int bit[200010];
int a[200010];
int main() {
while(~scanf("%d", &n)) {
LL ans1 = 0, ans2 = 0, ans3 = 0;
int pre1 = 1, pre2 = 0, pre0 = 0, now = 1;
for(int i = 1; i <= n; i ++) {
scanf("%d", &a[i]);
if(a[i] == 0) {
pre1 = 1, pre2 = 0;
now = 1;
ans3 += 1LL*(i-pre0)*(n-i+1);
pre0 = i;
continue;
}
if(a[i] > 0) now *= 1;
if(a[i] < 0) now *= -1;
if(now > 0) ans1 += pre1, ans2 += pre2, pre1 ++;
else ans1 += pre2, ans2 += pre1, pre2 ++;
}
printf("%lld %lld %lld
", ans2, ans3, ans1);
}
return 0;
}
G-Swap Letters(思考)
あなたに2つの文字列をあげて、ただ‘a’’’’b’’‘a’’b’’’’’’’’‘a’’b’の2種類の文字だけあって、毎回操作してあなたはそれぞれ2つの文字列の中の2つの文字を選んで交換することができて、少なくとも何回の操作を聞いて2つの文字列を同じにすることができて、‘−1’‘−1’’’‘−1’’’’’’’’’’‘−1’’’’’’’’’’’’’’’
問題を解く構想:貪欲に解いて、位置が同じで文字が同じで交換しないで、それでは最後にa b ab abあるいはb a ba baのこのような文字の対しか残っていません.2対の同じa b ab abあるいはb a baは1回の交換を通じてこの2対を同じにすることができて、1対のa b abとb a baは2回交換する必要があります.直接答えを統計すればいいです.
AC_code:
#include
#pragma GCC optimize(2)
#define bug printf("*********
");
#define mem0(a) memset(a, 0, sizeof(a));
#define mem1(a) memset(a, -1, sizeof(a));
#define finf(a, n) fill(a, a+n, INF);
#define lowbit(x) x&-x
#define fuck(x) cout<
#define ios ios::sync_with_stdio(false);
#define pb(x) push_back(x)
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<LL, pair<int, LL> > LLppar;
typedef pair<int, int> par;
typedef pair<LL, int> LLpar;
const LL mod = 1e9 + 7;
const int inf = 1e9 + 7;
const LL INF = 1e18 + 7;
const int base = 131; //19260817 233 1331 1e9+7
const double eps = 0.000001;
int n;
char s[200010], t[200010];
vector<int> v1, v2;
int main() {
while(~scanf("%d", &n)) {
scanf("%s%s", s, t);
v1.clear(), v2.clear();
for(int i = 0; i < n; i ++) {
if(s[i] == t[i]) continue;
if(s[i] == 'a') v1.push_back(i+1);
else v2.push_back(i+1);
}
if(v1.size()%2 != v2.size()%2) printf("-1
");
else {
int sum = v1.size()/2 + v2.size()/2 + v1.size()%2*2;
printf("%d
", sum);
while(v1.size() >= 2) {
printf("%d %d
", v1[v1.size()-1], v1[v1.size()-2]);
v1.pop_back();
v1.pop_back();
}
while(v2.size() >= 2) {
printf("%d %d
", v2[v2.size()-1], v2[v2.size()-2]);
v2.pop_back();
v2.pop_back();
}
if(v1.size()) {
printf("%d %d
", v1[0], v1[0]);
printf("%d %d
", v1[0], v2[0]);
}
}
}
return 0;
}
H-Berland Prospect(リニアdp)
あなたに1つの増加する配列をあげて、あなたに1つの最も長い等差のシーケンスを探して、出力の長さ.
解題構想:d p[i][j]dp[i][j]dp[i][j]dp[i][j]は位置i iから公差k=a[j]−a[i]k=a[j]−a[i]k=a[j]−a[i]k=a[j]−a[i]の最長シーケンスを表し、では、後ろから、d p[i][j]=m a x(d p[i][j],m a x(2,d p[j][i d d x[a[j]+k]+1))dp[i][j]=max(dp[i][j],max(2,dp[j][idx[a[j]+k]+1))dp[i][j]=max(dp[i]+j],max(2,dp[j]+idx[a[j]+k]+1))があり、最大値を記録すればよい.注意:この問題カードm a p Q A Q mapQQQQQQQQはm a p map mapでT T Tを離散化し、二分して下書きを探す必要がある.
AC_code:
#include
#pragma GCC optimize(2)
#define bug printf("*********
");
#define mem0(a) memset(a, 0, sizeof(a));
#define mem1(a) memset(a, -1, sizeof(a));
#define finf(a, n) fill(a, a+n, INF);
#define lowbit(x) x&-x
#define fuck(x) cout<
#define ios ios::sync_with_stdio(false);
#define pb(x) push_back(x)
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<LL, pair<int, LL> > LLppar;
typedef pair<int, int> par;
typedef pair<LL, int> LLpar;
const LL mod = 1e9 + 7;
const int inf = 1e9 + 7;
const LL INF = 1e18 + 7;
const int base = 131; //19260817 233 1331 1e9+7
const double eps = 0.000001;
int n;
LL x[3010];
int dp[3010][3010];
int main() {
while(~scanf("%d", &n)) {
mem0(dp);
int cnt = 0;
for(int i = 0; i < n; i ++) {
scanf("%lld", &x[i]);
}
int ans = 2;
for(int i = n-1; i >= 0; i --) {
for(int j = i+1; j < n; j ++) {
LL k = x[j]-x[i];
int id = lower_bound(x, x+n, x[j]+k)-x;
if(x[id] != x[j]+k) {
dp[i][j] = 2;continue;
}
dp[i][j] = max(dp[i][j], max(2, dp[j][id]+1));
ans = max(ans, dp[i][j]);
//printf("dp[%d][%d] = %d dp[%d][%d] = %d
", i, j, dp[i][j], j, id, dp[j][id]);
}
}
printf("%d
", ans);
}
return 0;
}
I-Radio Stations(2-sat+テクニック建図)
n n n nの都市はラジオ局を配置しなければならなくて、第i iの都市は少なくともx i xiあるいはy i yi yi yi yiの中の1つをインストールしなければならなくて、次にあなたにp p p pのpのラジオ局をあげて、ラジオ局i i i i iの電力範囲はl i-r i li-ri li-ri riで、最後にmの制限条件があって、第i iの制限条件はラジオ局u i ui uiとv i viはすべてインストールすることができません最後に、n+m n+m n+mの条件を満たし、少なくとも1つの電力値f f f fが選択したk k局がl i<=f<=r i li<=f<=ri li<=f<=riを満たし、k k k kとf f f f f fを出力し、どのk局を選択したかを満たすように、局の設置案を見つけます.
解題構想:電力範囲の制限がなければ、この問題は裸の2−s a t 2−sat 2−sat問題であり、現在電力範囲の制限があり、これらの制限条件も2−s a t 2−sat 2−sat 2−satのモデルに変換する必要がある.ここのn+m n+mの条件の連辺方式は私は言わない.主に電力範囲の連辺方式について話している.W∗2 W*2 W∗2点を再構築し、点i i iは条件f>=i f>=i f>=iを選択したことを示し、逆に点i+Wi+Wi+WはfAC_code:
#include
#pragma GCC optimize(2)
#define bug printf("*********
");
#define mem0(a) memset(a, 0, sizeof(a));
#define mem1(a) memset(a, -1, sizeof(a));
#define finf(a, n) fill(a, a+n, INF);
#define lowbit(x) x&-x
#define fuck(x) cout<
#define ios ios::sync_with_stdio(false);
#define pb(x) push_back(x)
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<LL, pair<int, LL> > LLppar;
typedef pair<int, int> par;
typedef pair<LL, int> LLpar;
const LL mod = 998244353;
const int inf = 1e9 + 7;
const LL INF = 1e18 + 7;
const int base = 131; //19260817 233 1331 1e9+7
const double eps = 0.000001;
const int MX = 2e6+5;
int n, p, M, m;
int cnt, idx, scc, k, out;
int head[MX], dfn[MX], low[MX], instack[MX], belong[MX];
int pos[MX], du[MX], output[MX];
stack<int> st;
vector<int> v[MX];
struct edge {
int to;
int nxt;
}e[MX*6];
void add(int u, int v) {
//printf("------%d %d
", u, v);
e[cnt].to = v;
e[cnt].nxt = head[u];
head[u] = cnt ++;
}
void tarjan(int k) {
dfn[k] = low[k] = idx ++; ///idx 1
instack[k] = 1;
st.push(k);
for(int i = head[k]; i != -1; i = e[i].nxt) {
int en = e[i].to;
if(!dfn[en]) {
tarjan(en);
low[k] = min(low[en], low[k]);
} else if(instack[en]) {
low[k] = min(low[k], dfn[en]);
}
}
if(dfn[k] == low[k]) {
scc ++; /// ( )
int v;
do {
v = st.top();
st.pop();
instack[v] = 0;
belong[v] = scc;
} while(k != v);
}
}
int pt[MX];
void topo() {
mem0(pt);
queue<int> q;
for(int i = 1; i <= scc; i ++) {
if(du[i] == 0) q.push(i);
}
while(!q.empty()) {
int u = q.front();q.pop();
if(pt[u] == 0) {
pt[u] = 1;
pt[pos[u]] = 2;
}
for(int i = 0; i < v[u].size(); i ++) {
int en = v[u][i];
du[en] --;
if(du[en] == 0) q.push(en);
}
}
out = 0;
for(int i = 1; i <= p; i ++) {
if(pt[belong[i]] == 1) output[out ++] = i;
}
}
bool solve() {
while(!st.empty()) st.pop();
for(int i = 1; i <= 2*(p+M); i ++) {
v[i].clear();
if(!dfn[i]) tarjan(i);
}
//printf("-----------%d
", scc);
for(int i = 1; i <= p; i ++) {
if(belong[i] == belong[i+p]) return 0;
pos[belong[i]] = belong[i+p];
pos[belong[i+p]] = belong[i];
}
for(int i = p*2+1; i <= p*2+M; i ++) {
if(belong[i] == belong[i+M]) return 0;
pos[belong[i]] = belong[i+M];
pos[belong[i+M]] = belong[i];
}
for(int i = 1; i <= p*2+M*2; i ++) {
for(int j = head[i]; ~j; j = e[j].nxt) {
int en = e[j].to;
if(belong[i] != belong[en]) {
du[belong[i]] ++;
v[belong[en]].pb(belong[i]);
}
}
}
topo();
return 1;
}
void init() {
mem1(head);
cnt = scc = 0;
idx = 1;
mem0(dfn); mem0(low); mem0(instack); mem0(belong);
mem0(pos); mem0(du); mem0(output);
}
int l[400010], r[400010];
int main() {
int x, y;
while(~scanf("%d%d%d%d", &n, &p, &M, &m)) {
init();
for(int i = 0; i < n; i ++) {
scanf("%d%d", &x, &y);
add(x+p, y), add(y+p, x);
}
int id = p*2;
for(int i = 1; i <= M; i ++) {
if(i > 1) add(id+i, id+i-1);
if(i < M) add(id+i+M, id+i+M+1);
}
for(int i = 1; i <= p; i ++) {
scanf("%d%d", &x, &y);
l[i] = x, r[i] = y;
add(i, id+x);
add(id+x+M, i+p);
add(i, id+y+M+1);
add(id+y+1, i+p);
}
for(int i = 0; i < m; i ++) {
scanf("%d%d", &x, &y);
add(x, y+p), add(y, x+p);
}
if(solve()) {
int L = 0;
for(int i = 0; i < out; i ++) {
L = max(L, l[output[i]]);
}
printf("%d %d
", out, L);
for(int i = 0; i < out; i ++) {
printf("%d ", output[i]);
}
printf("
");
}else printf("-1
");
}
return 0;
}
J-Monocarp and T-Shirts(期待+反発)
よく知られているように、ACMを打つと服を出すことができます.あなたはn試合があります.試合ごとにPの確率でx-1の大きさの服を手に入れます.Qの確率でx+1の大きさの服を手に入れます.だから、1-P-Qの確率でxの大きさの服を手に入れます.同時に、n人の友达がn枚の大きさの異なる服を手に入れたいと思っています.友达の数の期待を満たすことができます.
解題構想:友人の個数の期待を満たす=友人ごとに満たされる確率の和であるため,友人ごとに満たされる確率を求めればよい.確率を計算するときは反発すればよい(0.8 0.8 0.8の確率でx xを満たすと同時に0.7 0.7の確率でx x xを満たす.2つの確率は異なるイベントであるため、p(x)=0.8+0.7−0.8∗0.7 p(x)=0.8+0.7*0.7 p(x)=0.8+0.7−0.8∗0.7 p(x)=0.8+0.7−0.8*0.7 p(x)=0.8+0.7−0.8∗0.7)
AC_code:
#include
#pragma GCC optimize(2)
#define bug printf("*********
");
#define mem0(a) memset(a, 0, sizeof(a));
#define mem1(a) memset(a, -1, sizeof(a));
#define finf(a, n) fill(a, a+n, INF);
#define lowbit(x) x&-x
#define fuck(x) cout<
#define ios ios::sync_with_stdio(false);
#define pb(x) push_back(x)
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<LL, pair<int, LL> > LLppar;
typedef pair<int, int> par;
typedef pair<LL, int> LLpar;
const LL mod = 998244353;
const int inf = 1e9 + 7;
const LL INF = 1e18 + 7;
const int base = 131; //19260817 233 1331 1e9+7
const double eps = 0.000001;
int n;
LL p, q;
int a [200010];
LL ksm(LL a, int b) {
LL ans = 1;
while(b) {
if(b&1) ans = ans*a%mod;
a = a*a%mod;
b /= 2;
}
return ans;
}
int main() {
while(~scanf("%d%lld%lld", &n, &p, &q)) {
for(int i = 0; i < n; i ++) {
scanf("%d", &a[i]);
}
sort(a, a+n);
LL r = (1000000LL-p-q)*ksm(1000000, mod-2)%mod;
p = p*ksm(1000000, mod-2)%mod;
q = q*ksm(1000000, mod-2)%mod;
LL sum = 0;
for(int i = 0; i < n; i ++) {
int left = 0, right = 0;
if(i > 0 && a[i-1]+1 == a[i]) left = 1;
if(i < n-1 && a[i+1]-1 == a[i]) right = 1;
sum += r;
if(left) sum += q - r*q;
if(right) sum += p - r*p;
if(left && right) sum += r*p%mod*q%mod - p*q;
sum = (sum%mod+mod)%mod;
}
printf("%lld
", sum);
}
return 0;
}
K-Moonbound(アナログ)
最后にi行目j列を(i+j)%2==0 stand:stoneにして、11の充填を1つ选ぶことができて、2*2の充填を选ぶことができて、ある位置はそれを満たすことができて、后者は隣接してすでに充填されています.
問題を解く構想:シミュレーションすればよい
AC_code:
#include
const int inf = 1e9+7;
using namespace std;
int n;
int main() {
while(~scanf("%d", &n)) {
int k = 3*n*n/4;
printf("%d
", k);
for(int i = n-1; i >= 1; i -= 2) {
for(int j = 1; j <= n; j ++) {
if((i+j)%2 == 0) {
printf("1 %d %d 1
", i, j);
printf("1 %d %d 1
", i+1, j+1);
printf("2 %d %d 2
", i, j);
}
}
}
}
return 0;
}
L-Printer(暴力)
2阶建てのビルがあって、上に下りてk k k时间を必要として、毎阶の隣の位置の移动は1 1 1时间を使って、ある位置からすべての1 1 1 1位置の时间の最大値の最小値と最大値を求めます.
問題解決の考え方:暴力
AC_code:
#include
const int inf = 1e9+7;
using namespace std;
int n, k;
char s1[1010], s2[1010];
int main() {
while(~scanf("%d%d", &n, &k)) {
scanf("%s%s", s1, s2);
int ans = inf, idx1, idx2;
for(int i = 0; i < n; i ++) {
int sum1 = 0, sum2 = 0;
for(int j = 0; j < n; j ++) {
if(s1[j] == '1') sum1 = max(sum1, abs(j-i)), sum2 = max(sum2, i+j+1+1+k);
}
for(int j = 0; j < n; j ++) {
if(s2[j] == '1') sum2 = max(sum2, abs(j-i)), sum1 = max(sum1, i+j+1+1+k);
}
if(sum1 < ans) {
idx1 = 2, idx2 = i+1;
ans = sum1;
}
if(sum2 < ans) {
idx1 = 1, idx2 = i+1;
ans = sum2;
}
}
printf("%d
%d %d
", ans, idx1, idx2);
}
return 0;
}