2018 ICPC Nanjing Regional Contest(M待補)
阿新 • • 發佈:2021-01-19
A. Adrien and Austin
題意:博弈。n個石子,每次可以選連續的 1-k 個。問誰贏。
思路:簽到題。但我沒簽上來。還是開局兩小時後隊友簽上的。
AC程式碼:
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9+7;
const int MX = 1e6+7;
int mon[505];
int main()
{
int n, k;
cin>>n>>k;
if(n == 0 || (k == 1 && n%2 == 0)) puts("Austin");
else puts("Adrien");
return 0;
}
D. Country Meow
題意:題意極簡。n個三維的點。 找到空間中的一個點,使得到這n個點的最大歐氏距離最小。求最小距離。
思路:看了一眼感覺是二分套二分套二分。隊友糾正了一下,應該是三分。沒錯確實是三分。分別二分列舉三維的座標。距離變化是一個凹函式。所以三分找凹點就好了。三維座標那就套三層三分。每一層列舉一個座標。然後O(n) check 一下。資料小。時間是可以接受的。不過三分還是寫的不熟。還有模擬退火和最小圓覆蓋。待補待補!!
AC程式碼:
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int mod = 1e9+7;
struct node{
double x,y,z;
}p[1005];
int n;
double dis(double xx,double yy,double zz,int i){
double x = xx-p[i].x;
double y = yy-p[i].y;
double z = zz-p[i].z;
return sqrt(x*x+y* y+z*z);
}
double check(double xx,double yy,double zz){
double res = 0;
for(int i = 0 ;i < n ; i ++){
res = max(dis(xx,yy,zz,i),res);
}
return res;
}
double s3(double xx,double yy){
double l = -1e5+7;
double r = 1e5+7;
const double EPS = 1e-9;
double res = 1e18;
while(r - l > EPS) {
double lmid = l + (r - l) / 3;
double rmid = r - (r - l) / 3;
double lans = check(lmid,xx,yy),rans = check(rmid,xx,yy);
// 求凹函式的極小值
res = min(lans,rans);
if(lans <= rans) r = rmid;
else l = lmid;
}
return res;
}
double s2(double xx){
double l = -1e5+7;
double r = 1e5+7;
const double EPS = 1e-9;
double res = 1e18;
while(r - l > EPS) {
double lmid = l + (r - l) / 3;
double rmid = r - (r - l) / 3;
double lans = s3(lmid,xx),rans = s3(rmid,xx);
// 求凹函式的極小值
res = min(lans,rans);
if(lans <= rans) r = rmid;
else l = lmid;
}
return res;
}
double s1(){
double l = -1e5+7;
double r = 1e5+7;
const double EPS = 1e-9;
double res = 1e18;
while(r - l > EPS) {
double lmid = l + (r - l) / 3;
double rmid = r - (r - l) / 3;
double lans = s2(lmid),rans = s2(rmid);
// 求凹函式的極小值
res = min(lans,rans);
if(lans <= rans) r = rmid;
else l = lmid;
}
return res;
}
signed main(){
cin>>n;
for(int i = 0 ; i < n ; i ++)
cin>>p[i].x>>p[i].y>>p[i].z;
printf("%.15f",s1());
return 0;
}
G. Pyramid
題意:數n層有多少個三角形。斜著的也算!!
思路:先手畫幾個。測試資料這麼多,顯然是要找O(1)的公式。剛開始想的是求邊長為1的個數,為2的個數。。。然後加起來。越推越複雜。然後發現直接找答案的方程不就完了嘛。一個很經典的想法。
原數列:0,1,5,15,35,70,126,210…
做差:1,4,10,20,35,56…
再做差:3,4,5,6,7…
再做差:1,1,1,1…
終於相等了。做差n次之後相等。說明,答案計算公式的最高次是n+1次。
然後就帶值解出係數就好了 ans = ax4+bx3+cx2+dx+e
不過有人直接找出 ans =n * (n + 1) * (n + 2) * (n + 3) / 24 ,amazing啊。
AC程式碼:
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int mod = 1e9+7;
int qpow(int a,int b){
int res =1;
int tmp = a;
while(b){
if(b&1) res = (res*tmp) %mod;
tmp = (tmp*tmp)%mod;
b >>= 1;
}
return res;
}
int cal(int n){
return ((((qpow(n, 4)+6*qpow(n, 3))%mod+11*qpow(n, 2))%mod+6*n)%mod)*qpow(24, mod-2)%mod;
}
signed main(){
int t;
scanf("%lld",&t);
for(int i = 0 ; i < t ; i ++){
int n;
scanf("%lld",&n);
printf("%lld\n",cal(n));
}
return 0;
}
I. Magic Potion
題意:n 個 hero。m 個 monster。每個英雄有一個可以擊敗的怪物列表。但是最多隻能殺一個。然後還有k個魔法藥水。魔法藥水可以讓一個 英雄 多擊殺一個怪物。每個人最多喝一瓶。
思路:顯然是二分圖匹配。但是最開始想的是。先求一個最大匹配,然後把匹配過的刪掉,再匹配一次。和k取min。但是wa了。因為刪掉一個點損失太大了,就沒有了和別人匹配的機會。不過還好很快救回來了。把每個英雄拆成兩個。然後再求最大匹配。再和 原答案+k 取min。交了一發過了。這題網路流也可以做。待補待補。
AC程式碼:
#include <bits/stdc++.h>
#define int long long
using namespace std;
/* ***************************************************
二分圖匹配(匈牙利演算法的DFS實現)
INIT:G[][]兩邊定點劃分的情況
CALL:res=Hungary();輸出最大匹配數
優點:適於稠密圖,DFS找增廣路快,實現簡潔易於理解
時間複雜度:O(VE);
*****************************************************/
const int MAXN = 1010;
int uN,vN;//u,v數目
int G[MAXN][MAXN];//編號是1~n的
int linker[MAXN];
bool used[MAXN];
int mark[MAXN];
bool dfs(int u){
int v;
for(v=1;v<=vN;v++){
if(mark[v]) continue;
if(G[u][v]&&!used[v]){
used[v]=true;
if(linker[v]==-1||dfs(linker[v])){
linker[v]=u;
return true;
}
}
}
return false;
}
int Hungary()
{
int res=0;
int u;
memset(linker,-1,sizeof(linker));
for(u=1;u<=uN;u++){
memset(used,false,sizeof(used));
if(dfs(u)) res++;
}
return res;
}
signed main(){
int n,m,k;
cin>>n>>m>>k;
uN = n; vN = m;
for(int i = 1 ; i <= n ;i ++){
int cnt; cin>>cnt;
for(int j = 0 ; j < cnt ; j ++){
int x;
cin>>x;
G[i][x] = 1;
G[i+n][x] = 1;
}
}
int res1 = Hungary();
uN = 2*n;
int res2 = Hungary();
int res = min(res2,res1+k);
cout<<res<<endl;
return 0;
}
J. Prime Game
題意:一個數組,求出所有子區間的數連乘之後的不同質因子的個數之和。
思路:對每個數進行質因子分解。然後計算每個質因子對最後和的貢獻。考慮一個質因子同時出現在了 a[i] 和 a[j],那麼在計算a[j]的時候,所有包含i的子區間貢獻在計算i的時候就已經計算過了。所有那麼新增的貢獻,就是經過j,且不經過i的子區間的個數。那麼也就是 (j-i)*(n-j+1)。如下:
陣列:[2,3,5,3,7]
對於第一個3,所有有貢獻的子區間是 [1,2],[1,3],[1,4],[1,5] , [2,2],[2,3],[2,4],[2,5]
對於第二個3,所有有貢獻的子區間是[3,4],[3,5],[4,4],[4,5]
還有一個需要注意的地方就是質因子處理到 sqrt(n)就行了。
AC程式碼:
#include <bits/stdc++.h>
#define ll long long
//#define int long long
using namespace std;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9+7;
const int MX = 1e6+7;
int prime[MX];
bool vis[MX];
int tot = 0;
void pre()
{
for(ll i = 2; i*i < MX; ++i) {
if(!vis[i])
prime[tot++] = i;
for(ll j = 0; j < tot && i * prime[j] < MX; ++j) {
vis[i*prime[j]] = 1;
if(i%prime[j] == 0)
break;
}
}
}
ll last[MX];
signed main(){
pre();
ll n;
while(cin>>n) {
ll ans = 0;
int tmp;
for(ll i = 1; i <= n; ++i) {
scanf("%d", &tmp);
for(ll j = 0; j < tot && prime[j]*prime[j] <= tmp; ++j) {
if(tmp%prime[j] == 0) {
while(tmp%prime[j] == 0) {
tmp /= prime[j];
}
ans = ans+((i-last[prime[j]])*(n-i+1));
last[prime[j]] = i;
}
}
if(tmp > 1){
//cout<<tmp<<endl;
ans = ans+((i-last[tmp])*(n-i+1));
last[tmp] = i;
}
}
cout<<ans<<endl;
}
return 0;
}
K. Kangaroo Puzzle
題意:一個地圖。1的地方都是袋鼠。0都是牆。碰到障礙不能走。袋鼠可以疊在一起。可以用 UDLR 控制所有袋鼠同時移動。要在1e5次操作之內把他們全部移動到一個點。
思路:每次選擇兩個點。讓其中一個去追另一個。直到最後只剩下1個。這樣可以保證會走到一起。因為地圖太小。操作次數也是很有限的。不會超。有點像CF div2 DE難度的樣子。至於一個點追另一個點,就暴力模擬。先找到一條路徑。然後放到佇列裡。然後開始追。一邊進一邊出。前面的總會碰到牆的。因為保證沒有環。所以可以追到他無路可走。
AC程式碼:
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int mod = 1e9+7;
int n,m;
int mp[50][50];
int dirr[][2] = {0,1,1,0,0,-1,-1,0};
char dirrr[] = {'R','D','L','U'};
int mark[50][50];
map<char,int> id;
string res = "";
void show(){
return;
for(int i = 0 ; i < n ; i ++){
for(int j = 0 ; j < m ; j ++){
cout<<mp[i][j];
}
cout<<endl;
}
}
bool check(){
int res = 0;
for(int i = 0 ; i < n ; i ++){
for(int j = 0 ; j < m ; j ++){
res += mp[i][j] == 1;
if(res == 2) return false;
}
}
return true;
}
void move_kangaroo(char dir){
if(dir == 'U'){
for(int i = 1 ; i < n ; i ++){
for(int j = 0 ; j < m ; j ++){
if(mp[i][j] == 1 && mp[i-1][j] != 0){
mp[i-1][j] = 1;
mp[i][j] = 2;
}
}
}
}
if(dir == 'D'){
for(int i = n-2 ; i >= 0 ; i --){
for(int j = 0 ; j < m ; j ++){
if(mp[i][j] == 1 && mp[i+1][j] != 0){
mp[i+1][j] = 1;
mp[i][j] = 2;
}
}
}
}
if(dir == 'L'){
for(int i = 0 ; i < n ; i ++){
for(int j = 1 ; j < m ; j ++){
if(mp[i][j] == 1 && mp[i][j-1] != 0){
mp[i][j-1] = 1;
mp[i][j] = 2;
}
}
}
}
if(dir == 'R'){
for(int i = 0 ; i < n ; i ++){
for(int j = m-2 ; j >= 0 ; j --){
if(mp[i][j] == 1 && mp[i][j+1] != 0){
mp[i][j+1] = 1;
mp[i][j] = 2;
}
}
}
}
}
string dfs(int x1,int y1,int x2,int y2,string dir){
if(x1 == x2 && y1 == y2) return dir;
//cout<<x1<<" "<<y1<<" "<<x2<<" "<<y2<<endl;
mark[x1][y1] = 1;
for(int i = 0 ; i < 4 ; i ++){
int x = x1+dirr[i][0];
int y = y1+dirr[i][1];
if( x >= 0 && x < n && y < m && y >= 0 && !mark[x][y] && mp[x][y]){
string dir2 = "";
dir2 += dirrr[i];
string tmp = dfs(x,y,x2,y2,dir2);
if(tmp != ""){
mark[x1][y1] = 0;
return dir+tmp;
}
}
}
mark[x1][y1] = 0;
string tmp = "";
return tmp;
}
void solve(){
int x1,x2,y1,y2;
while(!check()){
x1 = x2 = -1;
for(int i = 0 ; i < n ; i ++){
for(int j = 0 ; j < m ; j ++){
if(mp[i][j] == 1 && x1 == -1){
x1 = i;
y1 = j;
}else if(mp[i][j] == 1){
x2 = i;
y2 = j;
}
}
}
string tmp = "";
string path = dfs(x1,y1,x2,y2,tmp);
show();
queue<char> que;
for(int i = 0 ; i < path.size() ; i ++){
que.push(path[i]);
}
while(que.size()){
char now = que.front();que.pop();
int tox2 = x2+dirr[id[now]][0];
int toy2 = y2+dirr[id[now]][1];
if(tox2 >= 0 && tox2 < n && toy2 >=0 && toy2 < m && mp[tox2][toy2]){
x2 = tox2;
y2 = toy2;
que.push(now);
}
x1 = x1+dirr[id[now]][0];
y1 = y1+dirr[id[now]][1];
move_kangaroo(now);
res += now;
if(x1 == x2 && y1 == y2) break;
show();
}
}
show();
}
signed main(){
id['R'] = 0;
id['D'] = 1;
id['L'] = 2;
id['U'] = 3;
cin>>n>>m;
for(int i = 0 ; i < n ; i ++){
string s;
cin>>s;
for(int j = 0 ; j < m ; j ++){
mp[i][j] = s[j] - '0';
}
}
solve();
cout<<res<<endl;
return 0;
}
這程式碼也能過。真 · 玄學。
#include<bits/stdc++.h>
using namespace std;
int n,m;
char tmp[23],d[4]={'L','R','U','D'};
int main()
{
srand(6510561);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%s",tmp);
for(int i=1;i<=50000;i++) printf("%c",d[rand()%4]);
}