A
被题意给搞蒙了。。
这题的关键就是求出一个滚动区间内的最大值以及最大值更新了多少次。
可以考虑单调队列反向用:单调队列维护最小值,数组倒着入队,那么显然,队首元素就是最大值,然后队内元素个数就是更新了多少次。因为假设x是最大值,那么x之前的元素一定会在队列中,即x更新了队列中元素个数的次数
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
ll a[10000005], Q[10000005];
int main()
{
int T;
scanf("%d", &T);
while(T --){
ll n, m, k, p, q, r, mod;
scanf("%lld %lld %lld %lld %lld %lld %lld", &n, &m, &k, &p, &q, &r, &mod);
for(ll i = 1; i <= k; i ++)
scanf("%lld", &a[i]);
for(ll i = k + 1; i <= n; i ++)
a[i] = (p * a[i - 1] + q * i + r) % mod;
int rear = 0, head = 1;
ll ans1 = 0, ans2 = 0;
for(ll i = n; i > 0; i --){
while(rear >= head && a[Q[rear]] <= a[i]) //确定插入位置
rear --;
Q[++ rear] = i;
if(i + m - 1 <= n){ //还可以产生出长度为m的区间
while(Q[head] >= i + m) //缩头
head ++;
ans1 += a[Q[head]] ^ i;
ans2 += (rear - head + 1) ^ i;
}
}
printf("%lld %lld\n", ans1, ans2);
}
return 0;
}
C
这题对我来说好像有点难。。感觉我补这题的收获就是知道状压dp是什么东西了。一个东西选中与否显然可以用01表示,不使用开bool数组而是借助一个整数的各个位来做这件事,应该就是状压dp了。然后剩下的就是各种神仙操作了。。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1000000007;
int n, q, u, v, t;
int dp[1234][20], f[1234];
char s[20];
int main(){
for(int i = 1; i < 1024; i ++)
f[i] = f[i >> 1] + (i & 1);//初始化,f[i]存储i的二进制有多少个1
scanf("%d",&t);
while(t --){
scanf("%d %d", &n, &q);
memset(dp, 0, sizeof(dp));
for(int i = 0; i < (1 << n); i ++)
dp[i][0] = 1; // 边数为0的状态有1种,初始化为1
for(int i = 0; i < q; i ++){
scanf("%s %d %d", s, &u, &v);
u --, v --;//若输入 1,2 要状态表示
// 1:第一位二进制为1, 2:第二位二进制为1
//1=2的0次方 1=2的一次方 所以减减
//很神奇对吧,u减减后2的(u-1)的次方二进制下 1所在的位就是第u位
int Ss = (1 << u) + (1 << v); //上面减减后状态表示
for(int S = 0; S <= (1 << n) - 1; S ++){
//枚举各种点集状态
if((S & Ss) == Ss){//若这种状态包含输入的两个点则处理
for(int j = 1; j <= f[S]; j ++){
dp[S][j] += (s[0] == '+' ? dp[S ^ Ss][j - 1] : (mod - dp[S ^ Ss][j - 1]));
// dp[S^Ss][j-1] 是不包含这两个点 且边数减少一
if(dp[S][j] >= mod)
dp[S][j] -= mod;
}
}
}
for(int j = 1; j <= n / 2; j ++)
printf("%d%c", dp[(1 << n) - 1][j], " \n"[j == n / 2]);
}
}
return 0;
}
D
数学上来先打表2333,然后就可以发现k=1是特殊情况,其余的都输出k+5即可
#include <bits/stdc++.h>
using namespace std;
int main(){
int t;
cin >> t;
while(t --){
int k;
cin >> k;
if(k == 1){
cout << 5 << endl;
continue;
}
cout << 5 + k << endl;
}
return 0;
}
F
很棒的一道题,我做的时候一看到异或基本就被劝退了
A:Q的异或和,B:T的异或和,S:总的异或和
显然A^B = S,那么当S = 0时,也就意味着A=B,即平局
当S!=0是,那么不妨设S = 00001xxxxxxx。设1的位置为i,那么显然,在i之前的每一个位置,A的值与B的值相同,在i这个位置,有一个是1有一个是0.那么显然,先手的Q只要把该位为1的数取走一个就一定会获胜
#include <bits/stdc++.h>
using namespace std;
int main(){
int t;
scanf("%d", &t);
while(t --){
int n;
int x;
int sum = 0;
cin >> n;
for(int i = 0; i < n; i ++){
scanf("%d", &x);
sum ^= x;
}
int a, b;
for(int i = 0; i < n - 1; i ++)
scanf("%d %d", &a, &b);
if(sum == 0){
cout << 'D' << endl;
}
else{
cout << 'Q' << endl;
}
}
return 0;
}
G
这题乍一看我都想跑最短路了,然后经过大佬的层层分析后才知道,原来就是求一个凸包,点技能树咯~~
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
typedef long long ll;
int n, m;
int top, ma;
int ans[N];
struct node{
int x, y;
int pos;
}s[N], sta[N], w;
ll judge(node p1, node p2, node p0){ //面积公式判断正负值
ll ans = 1ll * (p1. x - p0. x) * (p2. y - p0. y) - 1ll * (p2. x - p0. x) * (p1. y - p0. y);
return ans;
}
bool cmp(node a, node b){
ll c = judge(w, b, a);//极角排序,同角度按距离从小到大排
if(b. x == a. x && b. y == a. y)
return a. pos > b. pos;
if(! c)
return pow(a. x - w. x, 2) + pow(a. y - w. y, 2) < pow(b. x - w. x, 2) + pow(b. y - w. y, 2);
return c > 0;
}
void Graham(){
sta[0] = s[0], sta[1] = s[1];
top = 2;
for(int i = 2; i < n; i ++){
while(top > 1 && judge(sta[top-2], s[i], sta[top - 1]) <= 0){
if(judge(sta[top-2], s[i], sta[top-1]) < 0 || sta[top - 1]. pos > s[i]. pos)
top --;
else break;
}
sta[top ++] = s[i];
}
for(int i = 0; i < top; i ++)
printf("%d%c", sta[i]. pos + 1, i == top - 1 ? '\n' : ' ');
}
int main(){
int t;
scanf("%d", &t);
while(t --){
scanf("%d", &n);
for(int i = 0; i < n; i ++){
scanf("%d %d", &s[i]. x, &s[i]. y);
s[i]. pos = i;
}
w = s[0];
sort(s + 1, s + n, cmp);
Graham();
}
return 0;
}