联赛模拟测试15
\(T1\) 游戏
思路分析
- 考场的时候用贪心贪过了,正确性是可以保障的,只是时间效率差了点。先排序,每一组默认选最小的,然后再看每一组大的里如果没出现过,就看与它同一组的可不可以换成它。考场吸氧了,所以可以过。
- 并查集和二分图也都可以过,后来又写了一遍二分图的,跑的飞快。当然需要一个小技巧:不必使用
memset
,只需改变一个 \(now\) 值,用 \(now\) 值判断vis
数组。
\(Code\ in\ the\ test\)
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<map>
#include<vector>
#define N 1000010
#define R register
using namespace std;
inline int read(){
int x = 0,f = 1;
char ch = getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
int n;
struct data{
int x,y;
inline bool operator <(const data &a)const{
return x==a.x ? y < a.y : x < a.x;
}
}a[N];
map<int,int>vis;
vector<int>v[N];
int main(){
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
n = read();
for(R int i = 1;i <= n;i++){
a[i].x = read(),a[i].y = read();
if(a[i].x > a[i].y)swap(a[i].x,a[i].y);
}
sort(a+1,a+1+n);
for(R int i = 1;i <= n;i++){
vis[a[i].x]++;
v[a[i].y].push_back(a[i].x);
}
for(R int i = 1;i <= n;i++){
if(!vis[a[i].y]){
if(vis[a[i].x]>1)vis[a[i].x]--,vis[a[i].y]++;
else if(vis[a[i].x]==1){
int mx = 0,res = 0;
for(R int j = 0;j < v[a[i].x].size();j++){
if(vis[v[a[i].x][j]]>mx)mx = vis[v[a[i].x][j]],res = j;
}
if(mx>1)vis[v[a[i].x][res]]--,v[a[i].x][res] = 0,vis[a[i].y]++;
}
}
}
int ans = 0;
for(R int i = 1;i <= n;i++){
if(!vis[i])break;
ans++;
}
printf("%d\n",ans);
return 0;
}
\(Better\ Code\)
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<map>
#include<vector>
#define N 1000010
#define R register
using namespace std;
inline int read(){
int x = 0,f = 1;
char ch = getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
int n,head[N],vis[N],now,match[N];
struct edge{
int to,next;
}e[N<<1];
int len;
void addedge(int u,int v){
e[++len].to = v;
e[len].next = head[u];
head[u] = len;
}
bool find(int x){
for(R int i = head[x];i;i = e[i].next){
int v = e[i].to;
if(vis[v]==now)continue;//小技巧
vis[v] = now;
if(!match[v]||find(match[v])){
match[v] = x;
return 1;
}
}
return 0;
}
int main(){
#if 0
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
#endif
n = read();
for(R int i = 1;i <= n;i++){
int x = read(),y = read();
addedge(x,i),addedge(y,i);
}
int ans = 0;
for(R int i = 1;i <= n;i++){
now = i;//不用memset
if(find(i))ans = i;
else break;
}
printf("%d\n",ans);
return 0;
}
\(T2\) 嘟嘟噜
思路分析
- 前几天刚考过一次约瑟夫问题,自信的写完然后看了一眼数据范围整个人傻掉。
- 原递推公式是 \(x=(x+m)\%i\)。打表的时候发现了需要快速算出下次取模的位置,然而没有想出来怎么写……
- 第 \(i\) 轮需要 \(\%i\),所以可以算出上一个答案在达到 \(i\) 时需要加几个 \(m\),当然不能超过 \(n-i\) 个。然后 \(i\) 就可以快速跳跃了
Code
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#define N 1000010
#define R register
using namespace std;
inline int read(){
int x = 0,f = 1;
char ch = getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
int n,m;
int main(){
#if 1
freopen("mayuri.in","r",stdin);
freopen("mayuri.out","w",stdout);
#endif
int t = read();
while(t--){
n = read(),m = read();
int x = 0;
for(R int i = 1;i <= n;i++){
int tmp = min((i-x)/m-1,n-i-1);
if(tmp>0&&tmp<n)x += tmp*m,i += tmp;
x = (x+m)%i;
}
printf("%d\n",x+1);
}
return 0;
}
\(T3\) 天才绅士少女助手克里斯蒂娜
思路分析
- 推柿子,因为挨个码柿子太费时间了所以不码了(
其实是推不动),最后答案就是 \(\sum x*\sum y-(\sum x*y)^2\),树状数组就行了,线段树也行,但能用树状数组为什么要用线段树呢
\(Code\)
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 1000010
#define R register
#define ll long long
using namespace std;
inline ll read(){
ll x = 0,f = 1;
char ch = getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int mod = 20170927;
int n,m;
ll c[N][3];
struct Speed{
ll x,y;
}v[N];
void update(ll x,ll val,int id){
for(;x<=n;x+=x&(-x))c[x][id] = (c[x][id]+val+mod)%mod;
}
ll query(ll x,int id){
ll res = 0;
for(;x;x-=x&(-x))res = (res+c[x][id])%mod;
return res;
}
ll get_ans(int l,int r,int id){
return (query(r,id)-query(l-1,id)+mod)%mod;
}
int main(){
#if 1
freopen("kurisu.in","r",stdin);
freopen("kurisu.out","w",stdout);
#endif
n = read(),m = read();
for(R int i = 1;i <= n;i++){
v[i].x = read(),v[i].y = read();
update(i,v[i].x*v[i].x%mod,0);
update(i,v[i].y*v[i].y%mod,1);
update(i,v[i].x*v[i].y%mod,2);
}
for(R int i = 1;i <= m;i++){
int opt = read();
if(opt==1){
ll p = read(),x = read(),y = read();
update(p,(x*x%mod-v[p].x*v[p].x%mod+mod)%mod,0);
update(p,(y*y%mod-v[p].y*v[p].y%mod+mod)%mod,1);
update(p,(x*y%mod-v[p].x*v[p].y%mod+mod)%mod,2);
v[p].x = x,v[p].y = y;
}else{
int l = read(),r = read();
ll a = get_ans(l,r,0),b = get_ans(l,r,1),c = get_ans(l,r,2);
ll ans = (a*b%mod-c*c%mod+mod)%mod;
printf("%lld\n",ans);
}
}
return 0;
}
\(T4\) 凤凰院凶真
思路分析
- \(LCS+LIS = LCIS\)。考场上写了个 \(O(n^4)\) 的暴力 \(DP\),结果因为忽略了等于,导致捆绑测试全部爆零,怒送 \(30\)分,kuku
- 正解是 \(O(n^2)\) 的,首先枚举 \(a[i]\),然后在遍历 \(b\) 数组的过程中只需要记录一个小于 \(a[i]\) 的且答案最优的 \(b\) 数组的位置既可
- 输出路径,只需记录每个状态是由哪个状态转移来的,递归输出即可
\(Code\)
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define R register
#define N 5010
using namespace std;
inline int read(){
int x = 0,f = 1;
char ch = getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
int n,m,a[N],b[N],f[N][N],mx,ans,path[N][N];
void Print(int x,int y){
if(!x)return;
Print(x-1,path[x][y]);
if(path[x][y]!=y)printf("%d ",b[y]);
}
int main(){
#if 1
freopen("phoenix.in","r",stdin);
freopen("phoenix.out","w",stdout);
#endif
n = read();
for(R int i = 1;i <= n;i++)a[i] = read();
m = read();
for(R int i = 1;i <= m;i++)b[i] = read();
for(R int i = 1;i <= n;i++){
for(R int j = 1,k = 0;j <= m;j++){
if(a[i]==b[j]){
f[i][j] = f[i-1][k]+1;
path[i][j] = k;
}else{
f[i][j] = f[i-1][j];
path[i][j] = j;
}
if(b[j]<a[i]&&f[i][j]>f[i][k])k = j;
}
}
for(R int i = 1;i <= m;i++){
if(f[n][i]>f[n][ans])ans = i;
}
printf("%d\n",f[n][ans]);
Print(n,ans);
return 0;
}
挂分:\(30pts+\)