A 签到
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
void solve()
{
int a,b;
cin >> a >> b;
int cnt = 0;
if(b % a) puts("0 0");
else cout << 1 << " " << b / a << endl;
}
int main()
{
int t;
cin >> t;
while (t -- ) solve();
return 0;
}
B签到
#include <iostream>
#include <cstring>
#include <algorithm>
#include <map>
using namespace std;
map<string,int> mp;
void solve()
{
string s;
cin >> s;
cout << mp[s] <<endl;
}
int main()
{
int t;
int cnt = 1;
for(char i = 'a';i <= 'z';i ++)
for(char j = 'a';j <= 'z';j ++)
if(i != j)
{
string a = "";
a += i;
a += j;
mp[a] = cnt ++;
}
cin >> t;
while (t -- ) solve();
return 0;
}
C签到
#include <iostream>
#include <cstring>
#include <algorithm>
#include <map>
using namespace std;
typedef long long LL;
LL ans = 0;
void solve()
{
string s;string t;
cin >> s >> t;
int n = s.size(),m = t.size();
for(int i = 0;i < m;i ++)
if(t[i] == 'a' && m == 1){
puts("1");
return ;
}
else if(t[i] == 'a' && m > 1) {
puts("-1");
return ;
}
//cout << n << endl;
cout << (1ll << n) << endl;
}
int main()
{
int t;
cin >> t;
while (t -- ) solve();
return 0;
}
D 思维
一开始想歪了,想通过局部去推公式
然后寄了,这个题目正确解法还是应该从操作本身入手,发现操作的本质,做这种题目应该多去模拟模拟,发现规律
#include <iostream>
#include <cstring>
#include <algorithm>
#include <map>
using namespace std;
typedef long long LL;
const int N = 2e5 + 5;
int n,a[N];
int b[N];
void solve()
{
cin >> n;
for (int i = 1; i <= n; i ++ ) cin >> a[i];
for(int i = n;i >= 1;i -= 2){
if(i == 1) break;
if(a[i - 1] > a[i])
swap(a[i - 1],a[i]);
}
//for (int i = 1; i <= n; i ++ ) cout << a[i] << endl;
for (int i = 1; i < n; i ++ )
if(a[i] > a[i + 1])
{
puts("NO");
return ;
}
puts("YES");
}
int main()
{
int t;
cin >> t;
while (t -- ) solve();
return 0;
}
E贪心
这题分类讨论三中情况
赛时写寄了,写寄的那部分是相邻俩个数的情况
一开始以为相邻的俩个数,进行一次操作会消除3的贡献,但是发现并非如此,可能一个数通过这样的操作,他就会变为0,之后就无法贡献次数了,索引,在相邻俩个数之间应该进行一步贪心;
可以发现对大的进行-2的操作,对小的就是-1的操作,如此我们可以先将大的和小的进行变为相同的操作,同时应该对大的进行-2操作进行一个去min的操作,因为对大的-2不能减为负数,如果这么操作之后x和y还有剩余的话,进行-3的操作
#include <iostream>
#include <cstring>
#include <algorithm>
#include <map>
using namespace std;
typedef long long LL;
const int N = 2e5 + 5;
int n,a[N];
void solve()
{
cin >> n;
for (int i = 1; i <= n; i ++ )
cin >> a[i];
//俩种位置贪心选
//隔山打牛
//俩个相邻的位置 -2,-1
int ans = 0x3f3f3f3f;
for (int i = 1; i <= n - 2; i ++ )
ans = min(ans,(a[i] + a[i + 2] + 1) / 2);
for (int i = 1; i <= n - 1; i ++ ){
//ans = min(ans,(a[i] + a[i + 1] + 2) / 3);
//错误的贡献法,因为当一个数变为0的时候
//他就贡献不了次数了
//所以不能按总值来算
int cur = 0;
int x = a[i], y = a[i + 1];
if (x < y) {
swap(x, y);
}
int cnt = min(x - y, (x + 1) / 2);
cur += cnt;
x -= 2 * cnt;
y -= cnt;
if (x > 0 && y > 0) {
cur += (x + y + 2) / 3;
}
ans = min(ans, cur);
}
sort(a + 1,a + n + 1);
ans = min(ans,(a[1] + 1) / 2 + (a[2] + 1) / 2);
cout << ans << endl;
}
int main()
{
int t;
//cin >> t;
t = 1;
while (t -- ) solve();
return 0;
}
F 思维
实现可以用树状数组或者直接维护
树状数组
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1005 * 1005;
char a[1005][1005];
int n,m,q;
int tr[N];
int lowbit(int x)
{
return x & -x;
}
void update(int x, int c) // 位置x加c
{
for (int i = x; i <= n * m; i += lowbit(i)) tr[i] += c;
}
int query(int x) // 返回前x个数的和
{
int res = 0;
for (int i = x; i; i -= lowbit(i)) res += tr[i];
return res;
}
void solve()
{
cin >> n >> m >> q;
for(int i = 1;i <= n;i ++)
for(int j = 1;j <= m;j ++){
cin >> a[i][j];
int t = i + (j - 1) * n;
if('*' == a[i][j]) update(t,1);
}
while(q --){
int x,y;
cin >> x >> y;
int t = x + (y - 1) * n;
if(a[x][y] == '*') {
update(t,-1);
a[x][y] = '.';
}
else {
update(t,1);
a[x][y] = '*';
}
int cnt = query(n * m);
cout << cnt - query(cnt) << endl;
}
}
int main()
{
int t;
t = 1;
while (t -- ) solve();
return 0;
}
直接维护
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1005 * 1005;
int a[N];
int n,m,q;
void solve()
{
cin >> n >> m >> q;
int sum = 0;
for(int i = 1;i <= n;i ++)
for(int j = 1;j <= m;j ++){
int t = i + (j - 1) * n;
char x;
cin >> x;
if(x == '*') {a[t] = 1;sum ++;}
}
int cnt = 0;
for(int i = 1;i <= sum;i ++) cnt += a[i];
int ans = sum - cnt;
//cnt 表示sum前面的个数
//sum 表示应该插的位置在哪
while (q -- ){
int x,y;
cin >> x >> y;
int t = x + (y - 1) * n;
if(a[t] == 0){
a[t] = 1;
if(t > sum) ans ++;
sum ++;
if(a[sum]) ans --;
}
else {
a[t] = 0;
if(t > sum) ans --;
if(a[sum]) ans ++;
sum --;
}
cout << ans << endl;
}
}
int main()
{
int t;
t = 1;
while (t -- ) solve();
return 0;
}
G top排序 + DP
top序的概念 在DAG中,任俩个点 i,j 要么 i 到 j,要么 j 到 i。
首先抽象出来题目的大意
如果一个点存在入度或者出度的话,每一个点的入度和出度都必须减一。
然后考虑DP,首先这是一个DAG,所以在DAG上DP,应该先进行一下top排序,故在top排序的时候进行状态转移 ———— 路径上的每一个点都应该满足前驱节点的出度大于一,后驱节点的入度大于二这样才能转移,注意DP的初始化
#include <iostream>
#include <cstring>
#include <algorithm>
#include <map>
using namespace std;
typedef long long LL;
const int N = 2e5 + 5,M = N << 1;
int h[N], e[M], ne[M], idx;
int n,a,b,m;
int in[N],out[N];
int q[N],tt = -1,hh;
int dp[N];//表示以i为节点的合法方案
int top[N];
void add(int a, int b) // 添加一条边a->b
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
void solve()
{
cin >> n >> m;
memset(h, -1, sizeof h);
for(int i = 1;i <= m;i ++){
cin >> a >> b;
add(a, b);
top[b] ++;
in[b] ++;
out[a] ++;
}
for(int i = 1;i <= n;i ++)
if(!top[i]){
q[++ tt] = i;
dp[i] = 1;//dp初始化
}
int ans = 1;
while(hh <= tt){
auto u = q[hh ++];
//cout << u << endl;
for(int i = h[u]; ~i ;i = ne[i]){
int j = e[i];
if(out[u] > 1 && in[j] > 1){
//cout << " ---- " << endl;
dp[j] = max(dp[j],dp[u] + 1);
ans = max(dp[j],ans);
//cout << dp[j] << endl;
}
if(!--top[j]){
q[++ tt] = j;
if(!dp[j]) dp[j] = 1;
}
}
}
cout << ans << endl;
}
int main()
{
int t;
//cin >> t;
t = 1;
while (t -- ) solve();
return 0;
}